summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/android/launcher3/Launcher.java21
-rw-r--r--src/com/android/launcher3/LauncherBackupAgentHelper.java46
-rw-r--r--src/com/android/launcher3/LauncherBackupHelper.java (renamed from src/com/android/launcher3/LauncherBackupAgent.java)254
-rw-r--r--src/com/android/launcher3/LauncherProvider.java2
-rw-r--r--src/com/android/launcher3/PagedView.java55
-rw-r--r--src/com/android/launcher3/TranslucentDecor.java82
-rw-r--r--src/com/android/launcher3/TransparentBars.java54
-rw-r--r--src/com/android/launcher3/WallpaperCropActivity.java4
-rw-r--r--src/com/android/launcher3/WallpaperPickerActivity.java4
-rw-r--r--src/com/android/launcher3/Workspace.java78
10 files changed, 383 insertions, 217 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 45c95736e..cfa554513 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -327,7 +327,7 @@ public class Launcher extends Activity
private BubbleTextView mWaitingForResume;
- protected TransparentBars mTransparentBars;
+ protected TranslucentDecor mTransparentDecor;
private HideFromAccessibilityHelper mHideFromAccessibilityHelper
= new HideFromAccessibilityHelper();
@@ -409,6 +409,7 @@ public class Launcher extends Activity
mStats = new Stats(this);
mAppWidgetManager = AppWidgetManager.getInstance(this);
+
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
@@ -422,11 +423,12 @@ public class Launcher extends Activity
Environment.getExternalStorageDirectory() + "/launcher");
}
+
checkForLocaleChange();
setContentView(R.layout.launcher);
- mTransparentBars = new TransparentBars(findViewById(R.id.launcher));
- mTransparentBars.requestTransparentBars(true);
+ mTransparentDecor = new TranslucentDecor(findViewById(R.id.launcher));
+ mTransparentDecor.requestTranslucentDecor(true);
setupViews();
grid.layout(this);
@@ -921,8 +923,8 @@ public class Launcher extends Activity
mWorkspace.getCustomContentCallbacks().onShow();
}
}
-
mWorkspace.updateInteractionForState();
+ mWorkspace.onResume();
}
@Override
@@ -985,14 +987,9 @@ public class Launcher extends Activity
public void setScrollY(int scrollY);
}
- // Add a fullscreen unpadded view to the workspace to the left all other screens.
- public QSBScroller addToCustomContentPage(View customContent) {
- return addToCustomContentPage(customContent, null);
- }
-
public QSBScroller addToCustomContentPage(View customContent,
- CustomContentCallbacks callbacks) {
- mWorkspace.addToCustomContentPage(customContent, callbacks);
+ CustomContentCallbacks callbacks, String description) {
+ mWorkspace.addToCustomContentPage(customContent, callbacks, description);
return mQsbScroller;
}
@@ -3466,7 +3463,7 @@ public class Launcher extends Activity
text.clear();
// Populate event with a fake title based on the current state.
if (mState == State.APPS_CUSTOMIZE) {
- text.add(getString(R.string.all_apps_button_label));
+ text.add(mAppsCustomizeTabHost.getCurrentTabView().getContentDescription());
} else {
text.add(getString(R.string.all_apps_home_button_label));
}
diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java
new file mode 100644
index 000000000..2b5059b72
--- /dev/null
+++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.backup.BackupAgentHelper;
+import android.app.backup.BackupManager;
+import android.content.Context;
+
+public class LauncherBackupAgentHelper extends BackupAgentHelper {
+
+ private static BackupManager sBackupManager;
+
+ /**
+ * Notify the backup manager that out database is dirty.
+ *
+ * <P>This does not force an immediate backup.
+ *
+ * @param context application context
+ */
+ public static void dataChanged(Context context) {
+ if (sBackupManager == null) {
+ sBackupManager = new BackupManager(context);
+ }
+ sBackupManager.dataChanged();
+ }
+
+
+ @Override
+ public void onCreate() {
+ addHelper(LauncherBackupHelper.LAUNCHER_PREFIX, new LauncherBackupHelper(this));
+ }
+}
diff --git a/src/com/android/launcher3/LauncherBackupAgent.java b/src/com/android/launcher3/LauncherBackupHelper.java
index f40238cec..9b901eea1 100644
--- a/src/com/android/launcher3/LauncherBackupAgent.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -30,7 +30,8 @@ import com.android.launcher3.backup.BackupProtos.Resource;
import com.android.launcher3.backup.BackupProtos.Screen;
import com.android.launcher3.backup.BackupProtos.Widget;
-import android.app.backup.BackupAgent;
+import android.app.backup.BackupDataInputStream;
+import android.app.backup.BackupHelper;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.BackupManager;
@@ -64,10 +65,11 @@ import java.util.zip.CRC32;
/**
* Persist the launcher home state across calamities.
*/
-public class LauncherBackupAgent extends BackupAgent {
+public class LauncherBackupHelper implements BackupHelper {
- private static final String TAG = "LauncherBackupAgent";
+ private static final String TAG = "LauncherBackupHelper";
private static final boolean DEBUG = false;
+ private static final boolean DEBUG_PAYLOAD = false;
private static final int MAX_JOURNAL_SIZE = 1000000;
@@ -79,6 +81,8 @@ public class LauncherBackupAgent extends BackupAgent {
public static final int IMAGE_COMPRESSION_QUALITY = 75;
+ public static final String LAUNCHER_PREFIX = "L";
+
private static final Bitmap.CompressFormat IMAGE_FORMAT =
android.graphics.Bitmap.CompressFormat.PNG;
@@ -130,26 +134,19 @@ public class LauncherBackupAgent extends BackupAgent {
private static final int SCREEN_RANK_INDEX = 2;
-
- private static final String[] ICON_PROJECTION = {
- Favorites._ID, // 0
- Favorites.MODIFIED, // 1
- Favorites.INTENT // 2
- };
+ private final Context mContext;
private HashMap<ComponentName, AppWidgetProviderInfo> mWidgetMap;
+ private ArrayList<Key> mKeys;
- /**
- * Notify the backup manager that out database is dirty.
- *
- * <P>This does not force an immediate backup.
- *
- * @param context application context
- */
- public static void dataChanged(Context context) {
+ public LauncherBackupHelper(Context context) {
+ mContext = context;
+ }
+
+ private void dataChanged() {
if (sBackupManager == null) {
- sBackupManager = new BackupManager(context);
+ sBackupManager = new BackupManager(mContext);
}
sBackupManager.dataChanged();
}
@@ -167,9 +164,8 @@ public class LauncherBackupAgent extends BackupAgent {
* @throws IOException
*/
@Override
- public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
- ParcelFileDescriptor newState)
- throws IOException {
+ public void performBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+ ParcelFileDescriptor newState) {
Log.v(TAG, "onBackup");
Journal in = readJournal(oldState);
@@ -183,10 +179,14 @@ public class LauncherBackupAgent extends BackupAgent {
Log.v(TAG, "lastBackupTime=" + lastBackupTime);
ArrayList<Key> keys = new ArrayList<Key>();
- backupFavorites(in, data, out, keys);
- backupScreens(in, data, out, keys);
- backupIcons(in, data, out, keys);
- backupWidgets(in, data, out, keys);
+ try {
+ backupFavorites(in, data, out, keys);
+ backupScreens(in, data, out, keys);
+ backupIcons(in, data, out, keys);
+ backupWidgets(in, data, out, keys);
+ } catch (IOException e) {
+ Log.e(TAG, "launcher backup has failed", e);
+ }
out.key = keys.toArray(BackupProtos.Key.EMPTY_ARRAY);
writeJournal(newState, out);
@@ -194,70 +194,76 @@ public class LauncherBackupAgent extends BackupAgent {
}
/**
- * Restore home screen from the restored data stream.
+ * Restore launcher configuration from the restored data stream.
*
* <P>Keys may arrive in any order.
*
- * @param data the key/value pairs from the server
- * @param versionCode the version of the app that generated the data
- * @param newState notes for the next backup
- * @throws IOException
+ * @param data the key/value pair from the server
*/
@Override
- public void onRestore(BackupDataInput data, int versionCode, ParcelFileDescriptor newState)
- throws IOException {
- Log.v(TAG, "onRestore");
- int numRows = 0;
- Journal out = new Journal();
-
- ArrayList<Key> keys = new ArrayList<Key>();
+ public void restoreEntity(BackupDataInputStream data) {
+ Log.v(TAG, "restoreEntity");
+ if (mKeys == null) {
+ mKeys = new ArrayList<Key>();
+ }
byte[] buffer = new byte[512];
- while (data.readNextHeader()) {
- numRows++;
String backupKey = data.getKey();
- int dataSize = data.getDataSize();
+ int dataSize = data.size();
if (buffer.length < dataSize) {
buffer = new byte[dataSize];
}
Key key = null;
- int bytesRead = data.readEntityData(buffer, 0, dataSize);
- if (DEBUG) {
- Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available");
- }
- try {
- key = backupKeyToKey(backupKey);
- switch (key.type) {
- case Key.FAVORITE:
- restoreFavorite(key, buffer, dataSize, keys);
- break;
-
- case Key.SCREEN:
- restoreScreen(key, buffer, dataSize, keys);
- break;
-
- case Key.ICON:
- restoreIcon(key, buffer, dataSize, keys);
- break;
-
- case Key.WIDGET:
- restoreWidget(key, buffer, dataSize, keys);
- break;
-
- default:
- Log.w(TAG, "unknown restore entity type: " + key.type);
- break;
- }
- } catch (KeyParsingException e) {
- Log.w(TAG, "ignoring unparsable backup key: " + backupKey);
+ int bytesRead = 0;
+ try {
+ bytesRead = data.read(buffer, 0, dataSize);
+ if (DEBUG) Log.d(TAG, "read " + bytesRead + " of " + dataSize + " available");
+ } catch (IOException e) {
+ Log.d(TAG, "failed to read entity from restore data", e);
+ }
+ try {
+ key = backupKeyToKey(backupKey);
+ switch (key.type) {
+ case Key.FAVORITE:
+ restoreFavorite(key, buffer, dataSize, mKeys);
+ break;
+
+ case Key.SCREEN:
+ restoreScreen(key, buffer, dataSize, mKeys);
+ break;
+
+ case Key.ICON:
+ restoreIcon(key, buffer, dataSize, mKeys);
+ break;
+
+ case Key.WIDGET:
+ restoreWidget(key, buffer, dataSize, mKeys);
+ break;
+
+ default:
+ Log.w(TAG, "unknown restore entity type: " + key.type);
+ break;
}
+ } catch (KeyParsingException e) {
+ Log.w(TAG, "ignoring unparsable backup key: " + backupKey);
}
+ }
+
+ /**
+ * Record the restore state for the next backup.
+ *
+ * @param newState notes about the backup state after restore.
+ */
+ @Override
+ public void writeNewStateDescription(ParcelFileDescriptor newState) {
// clear the output journal time, to force a full backup to
// will catch any changes the restore process might have made
+ Journal out = new Journal();
out.t = 0;
- out.key = keys.toArray(BackupProtos.Key.EMPTY_ARRAY);
+ out.key = mKeys.toArray(BackupProtos.Key.EMPTY_ARRAY);
writeJournal(newState, out);
- Log.v(TAG, "onRestore: read " + numRows + " rows");
+ Log.v(TAG, "onRestore: read " + mKeys.size() + " rows");
+ mKeys.clear();
}
/**
@@ -278,7 +284,7 @@ public class LauncherBackupAgent extends BackupAgent {
if (DEBUG) Log.d(TAG, "favorite savedIds.size()=" + savedIds.size());
// persist things that have changed since the last backup
- ContentResolver cr = getContentResolver();
+ ContentResolver cr = mContext.getContentResolver();
Cursor cursor = cr.query(Favorites.CONTENT_URI, FAVORITE_PROJECTION,
null, null, null);
Set<String> currentIds = new HashSet<String>(cursor.getCount());
@@ -346,7 +352,7 @@ public class LauncherBackupAgent extends BackupAgent {
if (DEBUG) Log.d(TAG, "screen savedIds.size()=" + savedIds.size());
// persist things that have changed since the last backup
- ContentResolver cr = getContentResolver();
+ ContentResolver cr = mContext.getContentResolver();
Cursor cursor = cr.query(WorkspaceScreens.CONTENT_URI, SCREEN_PROJECTION,
null, null, null);
Set<String> currentIds = new HashSet<String>(cursor.getCount());
@@ -408,15 +414,15 @@ public class LauncherBackupAgent extends BackupAgent {
private void backupIcons(Journal in, BackupDataOutput data, Journal out,
ArrayList<Key> keys) throws IOException {
// persist icons that haven't been persisted yet
- final LauncherAppState app = LauncherAppState.getInstanceNoCreate();
- if (app == null) {
- dataChanged(this); // try again later
+ final LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
+ if (appState == null) {
+ dataChanged(); // try again later
if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying icon backup");
return;
}
- final ContentResolver cr = getContentResolver();
- final IconCache iconCache = app.getIconCache();
- final int dpi = getResources().getDisplayMetrics().densityDpi;
+ final ContentResolver cr = mContext.getContentResolver();
+ final IconCache iconCache = appState.getIconCache();
+ final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
// read the old ID set
Set<String> savedIds = getSavedIdsByType(Key.ICON, in);
@@ -463,7 +469,7 @@ public class LauncherBackupAgent extends BackupAgent {
} else {
if (DEBUG) Log.d(TAG, "scheduling another run for icon " + backupKey);
// too many icons for this pass, request another.
- dataChanged(this);
+ dataChanged();
}
}
} catch (URISyntaxException e) {
@@ -527,15 +533,15 @@ public class LauncherBackupAgent extends BackupAgent {
// persist static widget info that hasn't been persisted yet
final LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
if (appState == null) {
- dataChanged(this); // try again later
+ dataChanged(); // try again later
if (DEBUG) Log.d(TAG, "Launcher is not initialized, delaying widget backup");
return;
}
- final ContentResolver cr = getContentResolver();
- final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(this);
- final PagedViewCellLayout widgetSpacingLayout = new PagedViewCellLayout(this);
+ final ContentResolver cr = mContext.getContentResolver();
+ final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(mContext);
+ final PagedViewCellLayout widgetSpacingLayout = new PagedViewCellLayout(mContext);
final IconCache iconCache = appState.getIconCache();
- final int dpi = getResources().getDisplayMetrics().densityDpi;
+ final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile();
if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx);
@@ -584,7 +590,7 @@ public class LauncherBackupAgent extends BackupAgent {
} else {
if (DEBUG) Log.d(TAG, "scheduling another run for widget " + backupKey);
// too many widgets for this pass, request another.
- dataChanged(this);
+ dataChanged();
}
}
}
@@ -812,7 +818,7 @@ public class LauncherBackupAgent extends BackupAgent {
if (info.icon != 0) {
widget.icon = new Resource();
Drawable fullResIcon = iconCache.getFullResIcon(provider.getPackageName(), info.icon);
- Bitmap icon = Utilities.createIconBitmap(fullResIcon, this);
+ Bitmap icon = Utilities.createIconBitmap(fullResIcon, mContext);
ByteArrayOutputStream os = new ByteArrayOutputStream();
if (icon.compress(IMAGE_FORMAT, IMAGE_COMPRESSION_QUALITY, os)) {
widget.icon.data = os.toByteArray();
@@ -849,43 +855,49 @@ public class LauncherBackupAgent extends BackupAgent {
* @return a Journal protocol bugffer
*/
private Journal readJournal(ParcelFileDescriptor oldState) {
- int fileSize = (int) oldState.getStatSize();
- int remaining = fileSize;
- byte[] buffer = null;
Journal journal = new Journal();
- if (remaining < MAX_JOURNAL_SIZE) {
- FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor());
- int offset = 0;
-
- buffer = new byte[remaining];
- while (remaining > 0) {
+ if (oldState == null) {
+ return journal;
+ }
+ FileInputStream inStream = new FileInputStream(oldState.getFileDescriptor());
+ try {
+ int remaining = inStream.available();
+ if (DEBUG) Log.d(TAG, "available " + remaining);
+ if (remaining < MAX_JOURNAL_SIZE) {
+ byte[] buffer = new byte[remaining];
int bytesRead = 0;
- try {
- bytesRead = inStream.read(buffer, offset, remaining);
- } catch (IOException e) {
- Log.w(TAG, "failed to read the journal", e);
- buffer = null;
- remaining = 0;
- }
- if (bytesRead > 0) {
- remaining -= bytesRead;
- } else {
- // act like there is not journal
- Log.w(TAG, "failed to read the journal");
- buffer = null;
- remaining = 0;
+ while (remaining > 0) {
+ try {
+ int result = inStream.read(buffer, bytesRead, remaining);
+ if (result > 0) {
+ if (DEBUG) Log.d(TAG, "read some bytes: " + result);
+ remaining -= result;
+ bytesRead += result;
+ } else {
+ // stop reading ands see what there is to parse
+ Log.w(TAG, "read error: " + result);
+ remaining = 0;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "failed to read the journal", e);
+ buffer = null;
+ remaining = 0;
+ }
}
- }
-
- if (buffer != null) {
- try {
- MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, fileSize));
- } catch (InvalidProtocolBufferNanoException e) {
- Log.d(TAG, "failed to read the journal", e);
- journal.clear();
+ if (DEBUG) Log.d(TAG, "journal bytes read: " + bytesRead);
+
+ if (buffer != null) {
+ try {
+ MessageNano.mergeFrom(journal, readCheckedBytes(buffer, 0, bytesRead));
+ } catch (InvalidProtocolBufferNanoException e) {
+ Log.d(TAG, "failed to read the journal", e);
+ journal.clear();
+ }
}
}
-
+ } catch (IOException e) {
+ Log.d(TAG, "failed to close the journal", e);
+ } finally {
try {
inStream.close();
} catch (IOException e) {
@@ -904,7 +916,7 @@ public class LauncherBackupAgent extends BackupAgent {
out.bytes += blob.length;
Log.v(TAG, "saving " + geKeyType(key) + " " + backupKey + ": " +
getKeyName(key) + "/" + blob.length);
- if(DEBUG) {
+ if(DEBUG_PAYLOAD) {
String encoded = Base64.encodeToString(blob, 0, blob.length, Base64.NO_WRAP);
final int chunkSize = 1024;
for (int offset = 0; offset < encoded.length(); offset += chunkSize) {
@@ -983,7 +995,7 @@ public class LauncherBackupAgent extends BackupAgent {
private AppWidgetProviderInfo findAppWidgetProviderInfo(ComponentName component) {
if (mWidgetMap == null) {
List<AppWidgetProviderInfo> widgets =
- AppWidgetManager.getInstance(this).getInstalledProviders();
+ AppWidgetManager.getInstance(mContext).getInstalledProviders();
mWidgetMap = new HashMap<ComponentName, AppWidgetProviderInfo>(widgets.size());
for (AppWidgetProviderInfo info : widgets) {
mWidgetMap.put(info.provider, info);
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 5a8f630bc..39afe1042 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -210,7 +210,7 @@ public class LauncherProvider extends ContentProvider {
}
// always notify the backup agent
- LauncherBackupAgent.dataChanged(getContext());
+ LauncherBackupAgentHelper.dataChanged(getContext());
}
private void addModifiedTime(ContentValues values) {
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index c8e34dda1..96d8c1928 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -32,6 +32,7 @@ import android.graphics.Rect;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -334,6 +335,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
// Hook up the page indicator
ViewGroup parent = (ViewGroup) getParent();
if (mPageIndicator == null && mPageIndicatorViewId > -1) {
@@ -347,9 +350,19 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
mPageIndicator.addMarkers(markers, mAllowPagedViewAnimations);
+ mPageIndicator.setOnClickListener(getPageIndicatorClickListener());
+ mPageIndicator.setContentDescription(getPageIndicatorDescription());
}
}
+ protected String getPageIndicatorDescription() {
+ return getCurrentPageDescription();
+ }
+
+ protected OnClickListener getPageIndicatorClickListener() {
+ return null;
+ }
+
protected void onDetachedFromWindow() {
// Unhook the page indicator
mPageIndicator = null;
@@ -649,6 +662,28 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
}
+ private void sendScrollAccessibilityEvent() {
+ AccessibilityManager am =
+ (AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (am.isEnabled()) {
+ AccessibilityEvent ev =
+ AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+ ev.getText().add("");
+ ev.setItemCount(getChildCount());
+ ev.setFromIndex(mCurrentPage);
+ int action = AccessibilityNodeInfo.ACTION_SCROLL_FORWARD;
+
+ if (getNextPage() >= mCurrentPage) {
+ action = AccessibilityNodeInfo.ACTION_SCROLL_FORWARD;
+ } else {
+ action = AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
+ }
+
+ ev.setAction(action);
+ sendAccessibilityEventUnchecked(ev);
+ }
+ }
+
// we moved this functionality to a helper function so SmoothPagedView can reuse it
protected boolean computeScrollHelper() {
if (mScroller.computeScrollOffset()) {
@@ -663,6 +698,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
invalidate();
return true;
} else if (mNextPage != INVALID_PAGE) {
+ sendScrollAccessibilityEvent();
+
mCurrentPage = Math.max(0, Math.min(mNextPage, getPageCount() - 1));
mNextPage = INVALID_PAGE;
notifyPageSwitchListener();
@@ -680,14 +717,11 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
}
onPostReorderingAnimationCompleted();
- // Notify the user when the page changes
- AccessibilityManager accessibilityManager = (AccessibilityManager)
+ AccessibilityManager am = (AccessibilityManager)
getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
- if (accessibilityManager.isEnabled()) {
- AccessibilityEvent ev =
- AccessibilityEvent.obtain(AccessibilityEvent.TYPE_VIEW_SCROLLED);
- ev.getText().add(getCurrentPageDescription());
- sendAccessibilityEventUnchecked(ev);
+ if (am.isEnabled()) {
+ // Notify the user when the page changes
+ announceForAccessibility(getCurrentPageDescription());
}
return true;
}
@@ -2133,6 +2167,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
focusedChild.clearFocus();
}
+ sendScrollAccessibilityEvent();
+
pageBeginMoving();
awakenScrollBars(duration);
if (immediate) {
@@ -2719,11 +2755,6 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setScrollable(true);
- if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
- event.setFromIndex(mCurrentPage);
- event.setToIndex(mCurrentPage);
- event.setItemCount(getChildCount());
- }
}
@Override
diff --git a/src/com/android/launcher3/TranslucentDecor.java b/src/com/android/launcher3/TranslucentDecor.java
new file mode 100644
index 000000000..b50c02268
--- /dev/null
+++ b/src/com/android/launcher3/TranslucentDecor.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3;
+
+import android.app.Activity;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+public class TranslucentDecor {
+ private static final int SYSTEM_UI_FLAG_TRANSPARENT_STATUS = 0x00001000;
+ private static final int SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION = 0x00002000;
+
+ // Replace with SDK constants when available.
+ public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
+ public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;
+
+ // Behave properly on early K builds.
+ public static final boolean SYSUI_SUPPORTED = !hasSystemUiFlag("ALLOW_TRANSIENT") &&
+ hasSystemUiFlag("TRANSPARENT_STATUS") &&
+ hasSystemUiFlag("TRANSPARENT_NAVIGATION");
+
+ public static final boolean WM_SUPPORTED =
+ hasWindowManagerFlag("TRANSLUCENT_STATUS") &&
+ hasWindowManagerFlag("TRANSLUCENT_NAVIGATION");
+
+ private final View mTarget;
+
+ public TranslucentDecor(View target) {
+ mTarget = target;
+ }
+
+ public void requestTranslucentDecor(boolean translucent) {
+ int sysui = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+ if (WM_SUPPORTED && mTarget.getContext() instanceof Activity) {
+ Window w = ((Activity) mTarget.getContext()).getWindow();
+ int wmFlags = FLAG_TRANSLUCENT_STATUS | FLAG_TRANSLUCENT_NAVIGATION;
+ if (translucent) {
+ w.addFlags(wmFlags);
+ } else {
+ w.clearFlags(wmFlags);
+ }
+ } else if (SYSUI_SUPPORTED) { // Remove when droidfood platform is updated
+ if (translucent) {
+ sysui |= SYSTEM_UI_FLAG_TRANSPARENT_STATUS | SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION;
+ }
+ }
+ mTarget.setSystemUiVisibility(sysui);
+ }
+
+ private static boolean hasWindowManagerFlag(String name) {
+ try {
+ return WindowManager.LayoutParams.class.getField("FLAG_" + name) != null;
+ } catch (NoSuchFieldException e) {
+ return false;
+ }
+ }
+
+ private static boolean hasSystemUiFlag(String name) {
+ try {
+ return View.class.getField("SYSTEM_UI_FLAG_" + name) != null;
+ } catch (NoSuchFieldException e) {
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/TransparentBars.java b/src/com/android/launcher3/TransparentBars.java
deleted file mode 100644
index a12da9e6f..000000000
--- a/src/com/android/launcher3/TransparentBars.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.launcher3;
-
-import android.view.View;
-
-public class TransparentBars {
- private static final int SYSTEM_UI_FLAG_TRANSPARENT_STATUS = 0x00001000;
- private static final int SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION = 0x00002000;
-
- // Behave properly on early K builds. Replace with api check once sdk is baked.
- public static final boolean SUPPORTED = !hasSystemUiFlag("ALLOW_TRANSIENT")
- && hasSystemUiFlag("TRANSPARENT_STATUS")
- && hasSystemUiFlag("TRANSPARENT_NAVIGATION");
-
- private final View mTarget;
-
- public TransparentBars(View target) {
- mTarget = target;
- }
-
- public void requestTransparentBars(boolean transparent) {
- if (!SUPPORTED) return;
- int flags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
- | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
- | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
- if (transparent) {
- flags |= SYSTEM_UI_FLAG_TRANSPARENT_STATUS | SYSTEM_UI_FLAG_TRANSPARENT_NAVIGATION;
- }
- mTarget.setSystemUiVisibility(flags);
- }
-
- private static boolean hasSystemUiFlag(String name) {
- try {
- return View.class.getField("SYSTEM_UI_FLAG_" + name) != null;
- } catch (NoSuchFieldException e) {
- return false;
- }
- }
-}
diff --git a/src/com/android/launcher3/WallpaperCropActivity.java b/src/com/android/launcher3/WallpaperCropActivity.java
index 703db9a94..78c8964b6 100644
--- a/src/com/android/launcher3/WallpaperCropActivity.java
+++ b/src/com/android/launcher3/WallpaperCropActivity.java
@@ -102,8 +102,8 @@ public class WallpaperCropActivity extends Activity {
cropImageAndSetWallpaper(imageUri, null, finishActivityWhenDone);
}
});
- TransparentBars transparentBars = new TransparentBars(findViewById(R.id.wallpaper_root));
- transparentBars.requestTransparentBars(true);
+ TranslucentDecor transparentDecor = new TranslucentDecor(findViewById(R.id.wallpaper_root));
+ transparentDecor.requestTranslucentDecor(true);
}
public boolean enableRotation() {
diff --git a/src/com/android/launcher3/WallpaperPickerActivity.java b/src/com/android/launcher3/WallpaperPickerActivity.java
index 9702a3a67..82c9977c7 100644
--- a/src/com/android/launcher3/WallpaperPickerActivity.java
+++ b/src/com/android/launcher3/WallpaperPickerActivity.java
@@ -181,8 +181,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity {
protected void init() {
setContentView(R.layout.wallpaper_picker);
final WallpaperRootView root = (WallpaperRootView) findViewById(R.id.wallpaper_root);
- TransparentBars transparentBars = new TransparentBars(root);
- transparentBars.requestTransparentBars(true);
+ TranslucentDecor transparentDecor = new TranslucentDecor(root);
+ transparentDecor.requestTranslucentDecor(true);
mCropView = (CropView) findViewById(R.id.cropView);
mWallpaperStrip = findViewById(R.id.wallpaper_strip);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 5a8472c01..a81ada831 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -44,6 +44,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.IBinder;
import android.os.Parcelable;
+import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
@@ -52,6 +53,9 @@ import android.view.Display;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.TextView;
@@ -113,6 +117,7 @@ public class Workspace extends SmoothPagedView
private int mDefaultPage;
private ShortcutAndWidgetContainer mDragSourceInternal;
+ private static boolean sAccessibilityEnabled;
// The screen id used for the empty screen always present to the right.
private final static long EXTRA_EMPTY_SCREEN_ID = -201;
@@ -139,6 +144,7 @@ public class Workspace extends SmoothPagedView
CustomContentCallbacks mCustomContentCallbacks;
boolean mCustomContentShowing;
private float mLastCustomContentScrollProgress = -1f;
+ private String mCustomContentDescription = "";
/**
* The CellLayout that is currently being dragged over
@@ -318,11 +324,7 @@ public class Workspace extends SmoothPagedView
// Disable multitouch across the workspace/all apps/customize tray
setMotionEventSplittingEnabled(true);
-
- // Unless otherwise specified this view is important for accessibility.
- if (getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
- setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
- }
+ setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
@Override
@@ -450,9 +452,7 @@ public class Workspace extends SmoothPagedView
CellLayout cl = ((CellLayout) child);
cl.setOnInterceptTouchListener(this);
cl.setClickable(true);
- cl.setContentDescription(getContext().getString(
- R.string.workspace_description_format, getChildCount()));
-
+ cl.setImportantForAccessibility(ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO);
super.onChildViewAdded(parent, child);
}
@@ -555,7 +555,8 @@ public class Workspace extends SmoothPagedView
setCurrentPage(getCurrentPage() - 1);
}
- public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks) {
+ public void addToCustomContentPage(View customContent, CustomContentCallbacks callbacks,
+ String description) {
if (getPageIndexForScreenId(CUSTOM_CONTENT_SCREEN_ID) < 0) {
throw new RuntimeException("Expected custom content screen to exist");
}
@@ -570,7 +571,9 @@ public class Workspace extends SmoothPagedView
if (customContent instanceof Insettable) {
((Insettable)customContent).setInsets(mInsets);
}
+ customScreen.removeAllViews();
customScreen.addViewToCellLayout(customContent, 0, 0, lp, true);
+ mCustomContentDescription = description;
mCustomContentCallbacks = callbacks;
}
@@ -642,7 +645,6 @@ public class Workspace extends SmoothPagedView
return newId;
}
-
public CellLayout getScreenWithId(long screenId) {
CellLayout layout = mWorkspaceScreens.get(screenId);
return layout;
@@ -1039,6 +1041,9 @@ public class Workspace extends SmoothPagedView
mLauncher.updateVoiceButtonProxyVisible(false);
}
}
+ if (getPageIndicator() != null) {
+ getPageIndicator().setContentDescription(getPageIndicatorDescription());
+ }
}
protected CustomContentCallbacks getCustomContentCallbacks() {
@@ -1412,6 +1417,22 @@ public class Workspace extends SmoothPagedView
}
@Override
+ protected OnClickListener getPageIndicatorClickListener() {
+ AccessibilityManager am = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ if (!am.isTouchExplorationEnabled()) {
+ return null;
+ }
+ OnClickListener listener = new OnClickListener() {
+ @Override
+ public void onClick(View arg0) {
+ enterOverviewMode();
+ }
+ };
+ return listener;
+ }
+
+ @Override
protected void screenScrolled(int screenCenter) {
final boolean isRtl = isLayoutRtl();
super.screenScrolled(screenCenter);
@@ -1475,6 +1496,17 @@ public class Workspace extends SmoothPagedView
mWindowToken = null;
}
+ protected void onResume() {
+ if (getPageIndicator() != null) {
+ // In case accessibility state has changed, we need to perform this on every
+ // attach to window
+ getPageIndicator().setOnClickListener(getPageIndicatorClickListener());
+ }
+ AccessibilityManager am = (AccessibilityManager)
+ getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
+ sAccessibilityEnabled = am.isEnabled();
+ }
+
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (mFirstLayout && mCurrentPage >= 0 && mCurrentPage < getChildCount()) {
@@ -1852,6 +1884,14 @@ public class Workspace extends SmoothPagedView
private void setState(State state) {
mState = state;
updateInteractionForState();
+ updateAccessibilityFlags();
+ }
+
+ private void updateAccessibilityFlags() {
+ int accessible = mState == State.NORMAL ?
+ ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_YES :
+ ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
+ setImportantForAccessibility(accessible);
}
Animator getChangeStateAnimation(final State state, boolean animated, int delay, int snapPage) {
@@ -2036,8 +2076,11 @@ public class Workspace extends SmoothPagedView
}
public static void updateVisibility(View view) {
- if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != INVISIBLE) {
- view.setVisibility(INVISIBLE);
+ // We want to avoid the extra layout pass by setting the views to GONE unless
+ // accessibility is on, in which case not setting them to GONE causes a glitch.
+ int invisibleState = sAccessibilityEnabled ? GONE : INVISIBLE;
+ if (view.getAlpha() < ALPHA_CUTOFF_THRESHOLD && view.getVisibility() != invisibleState) {
+ view.setVisibility(invisibleState);
} else if (view.getAlpha() > ALPHA_CUTOFF_THRESHOLD
&& view.getVisibility() != VISIBLE) {
view.setVisibility(VISIBLE);
@@ -4308,10 +4351,19 @@ public class Workspace extends SmoothPagedView
public void syncPageItems(int page, boolean immediate) {
}
+ protected String getPageIndicatorDescription() {
+ String settings = getResources().getString(R.string.settings_button_text);
+ return getCurrentPageDescription() + ", " + settings;
+ }
+
protected String getCurrentPageDescription() {
int page = (mNextPage != INVALID_PAGE) ? mNextPage : mCurrentPage;
+ int delta = numCustomPages();
+ if (hasCustomContent() && getNextPage() == 0) {
+ return mCustomContentDescription;
+ }
return String.format(getContext().getString(R.string.workspace_scroll_format),
- page + 1, getChildCount());
+ page + 1 - delta, getChildCount() - delta);
}
public void getLocationInDragLayer(int[] loc) {