From 4f7eb4077e901a28b2b698060079292b56e991bd Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 10 Jul 2015 18:00:48 -0700 Subject: [PATCH 01/76] Fixing overview mode scale calculations. - Making the overview scale apply just like the spring loaded scale factor - Tweaking overview scale to make it not so small Change-Id: If93bac08609b0cfd9c9d9f8f6057498957378b56 --- res/values/config.xml | 4 ++-- src/com/android/launcher3/DeviceProfile.java | 19 +++++------------ src/com/android/launcher3/Workspace.java | 21 +++++++++++-------- .../WorkspaceStateTransitionAnimation.java | 3 ++- 4 files changed, 21 insertions(+), 26 deletions(-) diff --git a/res/values/config.xml b/res/values/config.xml index 73de794179..b2ba7a9869 100644 --- a/res/values/config.xml +++ b/res/values/config.xml @@ -2,8 +2,6 @@ 20 - - 80 false @@ -30,6 +28,8 @@ 80 + + 70 diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index a50540d26f..1c0743cbc1 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -25,6 +25,7 @@ import android.graphics.Paint.FontMetrics; import android.graphics.Point; import android.graphics.Rect; import android.util.DisplayMetrics; +import android.util.Size; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -56,7 +57,6 @@ public class DeviceProfile { private final int overviewModeBarItemWidthPx; private final int overviewModeBarSpacerWidthPx; private final float overviewModeIconZoneRatio; - private final float overviewModeScaleFactor; // Workspace private int desiredWorkspaceLeftRightMarginPx; @@ -136,8 +136,6 @@ public class DeviceProfile { res.getDimensionPixelSize(R.dimen.dynamic_grid_overview_bar_spacer_width); overviewModeIconZoneRatio = res.getInteger(R.integer.config_dynamic_grid_overview_icon_zone_percentage) / 100f; - overviewModeScaleFactor = - res.getInteger(R.integer.config_dynamic_grid_overview_scale_percentage) / 100f; iconDrawablePaddingOriginalPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_icon_drawable_padding); @@ -338,18 +336,11 @@ public class DeviceProfile { } } - Rect getOverviewModeButtonBarRect() { + int getOverviewModeButtonBarHeight() { int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx); zoneHeight = Math.min(overviewModeMaxIconZoneHeightPx, Math.max(overviewModeMinIconZoneHeightPx, zoneHeight)); - return new Rect(0, availableHeightPx - zoneHeight, 0, availableHeightPx); - } - - public float getOverviewModeScale(boolean isLayoutRtl) { - Rect workspacePadding = getWorkspacePadding(isLayoutRtl); - Rect overviewBar = getOverviewModeButtonBarRect(); - int pageSpace = availableHeightPx - workspacePadding.top - workspacePadding.bottom; - return (overviewModeScaleFactor * (pageSpace - overviewBar.height())) / pageSpace; + return zoneHeight; } // The rect returned will be extended to below the system ui that covers the workspace @@ -480,7 +471,7 @@ public class DeviceProfile { // Layout the Overview Mode ViewGroup overviewMode = launcher.getOverviewPanel(); if (overviewMode != null) { - Rect r = getOverviewModeButtonBarRect(); + int overviewButtonBarHeight = getOverviewModeButtonBarHeight(); lp = (FrameLayout.LayoutParams) overviewMode.getLayoutParams(); lp.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM; @@ -489,7 +480,7 @@ public class DeviceProfile { int maxWidth = totalItemWidth + (visibleChildCount-1) * overviewModeBarSpacerWidthPx; lp.width = Math.min(availableWidthPx, maxWidth); - lp.height = r.height(); + lp.height = overviewButtonBarHeight; overviewMode.setLayoutParams(lp); if (lp.width > totalItemWidth && visibleChildCount > 1) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 4a6b90afe0..267787be87 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -306,8 +306,9 @@ public class Workspace extends PagedView TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Workspace, defStyle, 0); mSpringLoadedShrinkFactor = - res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; - mOverviewModeShrinkFactor = grid.getOverviewModeScale(mIsRtl); + res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100.0f; + mOverviewModeShrinkFactor = + res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f; mOriginalDefaultPage = mDefaultPage = a.getInt(R.styleable.Workspace_defaultScreen, 1); a.recycle(); @@ -1952,15 +1953,17 @@ public class Workspace extends PagedView int getOverviewModeTranslationY() { DeviceProfile grid = mLauncher.getDeviceProfile(); - Rect overviewBar = grid.getOverviewModeButtonBarRect(); + Rect workspacePadding = grid.getWorkspacePadding(Utilities.isRtl(getResources())); + int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight(); - int availableHeight = getViewportHeight(); int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight()); - int offsetFromTopEdge = (availableHeight - scaledHeight) / 2; - int offsetToCenterInOverview = (availableHeight - mInsets.top - overviewBar.height() - - scaledHeight) / 2; - - return -offsetFromTopEdge + mInsets.top + offsetToCenterInOverview; + int workspaceTop = mInsets.top + workspacePadding.top; + int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom; + int overviewTop = mInsets.top; + int overviewBottom = getViewportHeight() - mInsets.bottom - overviewButtonBarHeight; + int workspaceOffsetTopEdge = workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2; + int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2; + return -workspaceOffsetTopEdge + overviewOffsetTopEdge; } /** diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index b8916a72bc..e908ccd812 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -211,8 +211,9 @@ public class WorkspaceStateTransitionAnimation { mOverlayTransitionTime = res.getInteger(R.integer.config_overlayTransitionTime); mSpringLoadedShrinkFactor = res.getInteger(R.integer.config_workspaceSpringLoadShrinkPercentage) / 100f; + mOverviewModeShrinkFactor = + res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f; mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha) / 100f; - mOverviewModeShrinkFactor = grid.getOverviewModeScale(Utilities.isRtl(res)); mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens(); } From 53f9672b30b8a36bfae9f6492f732ccfb87723a4 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 13 Jul 2015 19:54:53 -0700 Subject: [PATCH 02/76] Fixing widget restore > Widget restore active flag was not cleared when the app was downloaded > Icon from session info was not getting cached Bug: 22413379 Change-Id: Ie096b929252200675a76dadd8c25cc3aa433671b --- src/com/android/launcher3/IconCache.java | 35 +++++++++++++------- src/com/android/launcher3/LauncherModel.java | 10 ++++-- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 916418f18c..193f458b55 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -540,7 +540,7 @@ public class IconCache { mCache.put(cacheKey, entry); // Check the DB first. - if (!getEntryFromDB(componentName, user, entry, useLowResIcon)) { + if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { if (info != null) { entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext); } else { @@ -579,7 +579,14 @@ public class IconCache { Bitmap icon, CharSequence title) { removeFromMemCacheLocked(packageName, user); - CacheEntry entry = getEntryForPackageLocked(packageName, user, false); + ComponentKey cacheKey = getPackageKey(packageName, user); + CacheEntry entry = mCache.get(cacheKey); + + // For icon caching, do not go through DB. Just update the in-memory entry. + if (entry == null) { + entry = new CacheEntry(); + mCache.put(cacheKey, entry); + } if (!TextUtils.isEmpty(title)) { entry.title = title; } @@ -588,15 +595,18 @@ public class IconCache { } } + private static ComponentKey getPackageKey(String packageName, UserHandleCompat user) { + ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME); + return new ComponentKey(cn, user); + } + /** * Gets an entry for the package, which can be used as a fallback entry for various components. * This method is not thread safe, it must be called from a synchronized method. - * */ private CacheEntry getEntryForPackageLocked(String packageName, UserHandleCompat user, boolean useLowResIcon) { - ComponentName cn = new ComponentName(packageName, packageName + EMPTY_CLASS_NAME); - ComponentKey cacheKey = new ComponentKey(cn, user); + ComponentKey cacheKey = getPackageKey(packageName, user); CacheEntry entry = mCache.get(cacheKey); if (entry == null || (entry.isLowResIcon && !useLowResIcon)) { @@ -604,7 +614,7 @@ public class IconCache { boolean entryUpdated = true; // Check the DB first. - if (!getEntryFromDB(cn, user, entry, useLowResIcon)) { + if (!getEntryFromDB(cacheKey, entry, useLowResIcon)) { try { PackageInfo info = mPackageManager.getPackageInfo(packageName, 0); ApplicationInfo appInfo = info.applicationInfo; @@ -622,7 +632,8 @@ public class IconCache { // package updates. ContentValues values = newContentValues(entry.icon, entry.title.toString(), mPackageBgColor); - addIconToDB(values, cn, info, mUserManager.getSerialNumberForUser(user)); + addIconToDB(values, cacheKey.componentName, info, + mUserManager.getSerialNumberForUser(user)); } catch (NameNotFoundException e) { if (DEBUG) Log.d(TAG, "Application not installed " + packageName); @@ -667,14 +678,13 @@ public class IconCache { SQLiteDatabase.CONFLICT_REPLACE); } - private boolean getEntryFromDB(ComponentName component, UserHandleCompat user, - CacheEntry entry, boolean lowRes) { + private boolean getEntryFromDB(ComponentKey cacheKey, CacheEntry entry, boolean lowRes) { Cursor c = mIconDb.getReadableDatabase().query(IconDB.TABLE_NAME, new String[] {lowRes ? IconDB.COLUMN_ICON_LOW_RES : IconDB.COLUMN_ICON, IconDB.COLUMN_LABEL}, IconDB.COLUMN_COMPONENT + " = ? AND " + IconDB.COLUMN_USER + " = ?", - new String[] {component.flattenToString(), - Long.toString(mUserManager.getSerialNumberForUser(user))}, + new String[] {cacheKey.componentName.flattenToString(), + Long.toString(mUserManager.getSerialNumberForUser(cacheKey.user))}, null, null, null); try { if (c.moveToNext()) { @@ -685,7 +695,8 @@ public class IconCache { entry.title = ""; entry.contentDescription = ""; } else { - entry.contentDescription = mUserManager.getBadgedLabelForUser(entry.title, user); + entry.contentDescription = mUserManager.getBadgedLabelForUser( + entry.title, cacheKey.user); } return true; } diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index b60477fa00..0b67310fa9 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2163,7 +2163,11 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId, provider.provider); - int status = restoreStatus; + // The provider is available. So the widget is either + // available or not available. We do not need to track + // any future restore updates. + int status = restoreStatus & + ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; if (!wasProviderReady) { // If provider was not previously ready, update the // status and UI flag. @@ -3155,7 +3159,9 @@ public class LauncherModel extends BroadcastReceiver if (mUser.equals(widgetInfo.user) && widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) && packageSet.contains(widgetInfo.providerName.getPackageName())) { - widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; + widgetInfo.restoreStatus &= + ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY & + ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; widgets.add(widgetInfo); updateItemInDatabase(context, widgetInfo); } From c115e647ad79face0b57240fe3a7e7be2ce85145 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 20 Jul 2015 14:23:43 -0700 Subject: [PATCH 03/76] Shifting hotseat icons to aling with all-apps icon, if the hotseat size is different from the source device Bug: 19074056 Change-Id: I8d8517971a3cde0dc38f653d151daeba90fa38a0 --- .../launcher3/LauncherBackupHelper.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 8c6fedbdc2..8ac09d35de 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -153,6 +153,10 @@ public class LauncherBackupHelper implements BackupHelper { boolean restoreSuccessful; int restoredBackupVersion = 1; + // When migrating from a device which different hotseat configuration, the icons are shifted + // to center along the new all-apps icon. + private int mHotseatShift = 0; + public LauncherBackupHelper(Context context) { mContext = context; mExistingKeys = new HashSet(); @@ -285,10 +289,15 @@ public class LauncherBackupHelper implements BackupHelper { boolean isHotsetCompatible = false; if (currentProfile.allappsRank >= oldProfile.hotseatCount) { isHotsetCompatible = true; + mHotseatShift = 0; } - if ((currentProfile.hotseatCount >= oldProfile.hotseatCount) && - (currentProfile.allappsRank == oldProfile.allappsRank)) { + + if ((currentProfile.allappsRank >= oldProfile.allappsRank) + && ((currentProfile.hotseatCount - currentProfile.allappsRank) >= + (oldProfile.hotseatCount - oldProfile.allappsRank))) { + // There is enough space on both sides of the hotseat. isHotsetCompatible = true; + mHotseatShift = currentProfile.allappsRank - oldProfile.allappsRank; } return isHotsetCompatible && (currentProfile.desktopCols >= oldProfile.desktopCols) @@ -847,6 +856,11 @@ public class LauncherBackupHelper implements BackupHelper { throws IOException { Favorite favorite = unpackProto(new Favorite(), buffer, dataSize); + // If it is a hotseat item, move it accordingly. + if (favorite.container == Favorites.CONTAINER_HOTSEAT) { + favorite.screen += mHotseatShift; + } + ContentValues values = new ContentValues(); values.put(Favorites._ID, favorite.id); values.put(Favorites.SCREEN, favorite.screen); From df6ccf81960ab73d46570d56443e94c4df48e0c8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 20 Jul 2015 14:32:48 -0700 Subject: [PATCH 04/76] Resizing the bitmap before caching it in the DB > The icon size in the backup can be different that what is required by the device. We should resize the icon, before caching it. Bug: 22413328 Change-Id: Id77c53edf8ea5e95a2d32dbe22be553120279ebd --- src/com/android/launcher3/IconCache.java | 6 ++++-- src/com/android/launcher3/LauncherBackupHelper.java | 9 +++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index 916418f18c..b4ca515d86 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -649,7 +649,7 @@ public class IconCache { * @param dpi the native density of the icon */ public void preloadIcon(ComponentName componentName, Bitmap icon, int dpi, String label, - long userSerial) { + long userSerial, InvariantDeviceProfile idp) { // TODO rescale to the correct native DPI try { PackageManager packageManager = mContext.getPackageManager(); @@ -660,7 +660,9 @@ public class IconCache { // pass } - ContentValues values = newContentValues(icon, label, Color.TRANSPARENT); + ContentValues values = newContentValues( + Bitmap.createScaledBitmap(icon, idp.iconBitmapSize, idp.iconBitmapSize, true), + label, Color.TRANSPARENT); values.put(IconDB.COLUMN_COMPONENT, componentName.flattenToString()); values.put(IconDB.COLUMN_USER, userSerial); mIconDb.getWritableDatabase().insertWithOnConflict(IconDB.TABLE_NAME, null, values, diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 8c6fedbdc2..744f73bdad 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -601,10 +601,11 @@ public class LauncherBackupHelper implements BackupHelper { Bitmap icon = BitmapFactory.decodeByteArray(res.data, 0, res.data.length); if (icon == null) { Log.w(TAG, "failed to unpack icon for " + key.name); + } else { + if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name); + mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi, + "" /* label */, mUserSerial, mIdp); } - if (VERBOSE) Log.v(TAG, "saving restored icon as: " + key.name); - mIconCache.preloadIcon(ComponentName.unflattenFromString(key.name), icon, res.dpi, - "" /* label */, mUserSerial); } /** @@ -685,7 +686,7 @@ public class LauncherBackupHelper implements BackupHelper { Log.w(TAG, "failed to unpack widget icon for " + key.name); } else { mIconCache.preloadIcon(ComponentName.unflattenFromString(widget.provider), - icon, widget.icon.dpi, widget.label, mUserSerial); + icon, widget.icon.dpi, widget.label, mUserSerial, mIdp); } } From 86179020cc1a273d7e753d278fd844b90df456b0 Mon Sep 17 00:00:00 2001 From: bohu Date: Thu, 21 May 2015 08:20:38 -0700 Subject: [PATCH 05/76] Enable Launcher3 as default Home app The default Home app launcher2 is deprecated and cannot launch Nexus 6 or any other high resolution devices in emulator. This CL enables launcher3 as the default Home app, since it can handle Nexus 6 and is well maintained. Bug: 22609402 Change-Id: Iaba545fec218f79e40e0f72de5236ddadfabaa14 --- Android.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Android.mk b/Android.mk index 2e574783df..3ff542e612 100644 --- a/Android.mk +++ b/Android.mk @@ -36,7 +36,7 @@ LOCAL_AAPT_FLAGS := --auto-add-overlay LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := Launcher3 -LOCAL_OVERRIDES_PACKAGES := Launcher2 +LOCAL_OVERRIDES_PACKAGES := Home Launcher2 include $(BUILD_PACKAGE) From 107ea6345bff14ad172013783040bb127deacb61 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 20 Jul 2015 12:59:39 -0700 Subject: [PATCH 06/76] Adding rank column in backup > targetType was only added for M builds, so it can also be considered as v4 change. V3 which introduced minSpanX and minSpanY didn't contain targetType Bug: 22417713 Change-Id: I5c353674f7e0c2b5b4ab46e574fdb347d82028cd --- protos/backup.proto | 4 ++++ .../launcher3/LauncherBackupAgentHelper.java | 5 +++-- .../android/launcher3/LauncherBackupHelper.java | 15 ++++++++++++--- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/protos/backup.proto b/protos/backup.proto index d8d94e8cb5..6704e08ad4 100644 --- a/protos/backup.proto +++ b/protos/backup.proto @@ -101,7 +101,10 @@ message Favorite { optional string iconPackage = 16; optional string iconResource = 17; optional bytes icon = 18; + + // Added in backup version 4 optional TargetType targetType = 19 [default = TARGET_NONE]; + optional int32 rank = 20; } message Screen { @@ -121,6 +124,7 @@ message Widget { optional Resource icon = 4; optional Resource preview = 5; + // Added in backup version 3 // Assume that a widget is resizable upto 2x2 if no data is available optional int32 minSpanX = 6 [default = 2]; optional int32 minSpanY = 7 [default = 2]; diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index a92a889f9a..b7860d45bb 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -91,11 +91,12 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { LauncherAppState.getLauncherProvider().clearFlagEmptyDbCreated(); LauncherClings.synchonouslyMarkFirstRunClingDismissed(this); - // TODO: Update the backup set to include rank. + // Rank was added in v4. if (mHelper.restoredBackupVersion <= 3) { LauncherAppState.getLauncherProvider().updateFolderItemsRank(); - LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities(); } + + LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities(); } else { if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB"); LauncherAppState.getLauncherProvider().createEmptyDB(); diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 38a4bdeb46..39603ad1bf 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -75,7 +75,7 @@ public class LauncherBackupHelper implements BackupHelper { private static final boolean VERBOSE = LauncherBackupAgentHelper.VERBOSE; private static final boolean DEBUG = LauncherBackupAgentHelper.DEBUG; - private static final int BACKUP_VERSION = 3; + private static final int BACKUP_VERSION = 4; private static final int MAX_JOURNAL_SIZE = 1000000; // Journal key is such that it is always smaller than any dynamically generated @@ -107,6 +107,7 @@ public class LauncherBackupHelper implements BackupHelper { Favorites.SPANY, // 15 Favorites.TITLE, // 16 Favorites.PROFILE_ID, // 17 + Favorites.RANK, // 18 }; private static final int ID_INDEX = 0; @@ -126,6 +127,7 @@ public class LauncherBackupHelper implements BackupHelper { private static final int SPANX_INDEX = 14; private static final int SPANY_INDEX = 15; private static final int TITLE_INDEX = 16; + private static final int RANK_INDEX = 18; private static final String[] SCREEN_PROJECTION = { WorkspaceScreens._ID, // 0 @@ -441,7 +443,10 @@ public class LauncherBackupHelper implements BackupHelper { Key key = getKey(Key.FAVORITE, id); mKeys.add(key); final String backupKey = keyToBackupKey(key); - if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime) { + + // Favorite proto changed in v4. Backup again if the version is old. + if (!mExistingKeys.contains(backupKey) || updateTime >= mLastBackupRestoreTime + || restoredBackupVersion < 4) { writeRowToBackup(key, packFavorite(cursor), data); } else { if (DEBUG) Log.d(TAG, "favorite already backup up: " + id); @@ -648,7 +653,9 @@ public class LauncherBackupHelper implements BackupHelper { } else { Log.w(TAG, "empty intent on appwidget: " + id); } - if (mExistingKeys.contains(backupKey) && restoredBackupVersion >= BACKUP_VERSION) { + + // Widget backup proto changed in v3. So add it again if the original backup is old. + if (mExistingKeys.contains(backupKey) && restoredBackupVersion >= 3) { if (DEBUG) Log.d(TAG, "already saved widget " + backupKey); // remember that we already backed this up previously @@ -783,6 +790,7 @@ public class LauncherBackupHelper implements BackupHelper { favorite.spanX = c.getInt(SPANX_INDEX); favorite.spanY = c.getInt(SPANY_INDEX); favorite.iconType = c.getInt(ICON_TYPE_INDEX); + favorite.rank = c.getInt(RANK_INDEX); String title = c.getString(TITLE_INDEX); if (!TextUtils.isEmpty(title)) { @@ -870,6 +878,7 @@ public class LauncherBackupHelper implements BackupHelper { values.put(Favorites.CELLY, favorite.cellY); values.put(Favorites.SPANX, favorite.spanX); values.put(Favorites.SPANY, favorite.spanY); + values.put(Favorites.RANK, favorite.rank); if (favorite.itemType == Favorites.ITEM_TYPE_SHORTCUT) { values.put(Favorites.ICON_TYPE, favorite.iconType); From ce77f36affd0ac37dc79fe261f34e13ddccde1cf Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 24 Jul 2015 15:56:33 -0700 Subject: [PATCH 07/76] Don't obfuscate Launcher3 Change-Id: I86ac745602c0d3c09842ed9553f7589c232f5d23 --- proguard.flags | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/proguard.flags b/proguard.flags index 6a9d6f3457..a8e2b60929 100644 --- a/proguard.flags +++ b/proguard.flags @@ -1,3 +1,7 @@ +-keep,allowshrinking,allowoptimization class com.android.launcher3.** { + *; +} + -keep class com.android.launcher3.BaseRecyclerViewFastScrollBar { public void setWidth(int); public int getWidth(); @@ -63,4 +67,4 @@ -keep class com.android.launcher3.Workspace { public float getBackgroundAlpha(); public void setBackgroundAlpha(float); -} \ No newline at end of file +} From a9095e07ddd2318ceb81a2794219e07dd112df72 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 27 Jul 2015 13:35:33 -0700 Subject: [PATCH 08/76] Remove NPE inside WidgetsListAdapter during monkey actor test b/22518383 Change-Id: I1a683c49be93af150f7873390b723cee0d4ac5df --- src/com/android/launcher3/widget/WidgetsListAdapter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index d2ea252302..a54626a391 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -87,6 +87,9 @@ public class WidgetsListAdapter extends Adapter { @Override public int getItemCount() { + if (mWidgetsModel == null) { + return 0; + } return mWidgetsModel.getPackageSize(); } From d30e845d89a069a8c1ee687791030e8e981170b3 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Mon, 27 Jul 2015 18:24:45 -0700 Subject: [PATCH 09/76] Overlay support lib RV resources in Android.mk Note: after this change, ag/735298 can be reverted. b/22719084 Change-Id: I83caf8832790b1df0a90a1339f6b24f481c51661 --- Android.mk | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/Android.mk b/Android.mk index 3ff542e612..ec675c6630 100644 --- a/Android.mk +++ b/Android.mk @@ -23,16 +23,25 @@ include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-v4 \ + android-support-v7-recyclerview + LOCAL_SRC_FILES := $(call all-java-files-under, src) \ $(call all-java-files-under, WallpaperPicker/src) \ $(call all-proto-files-under, protos) -LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res $(LOCAL_PATH)/res + +LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/WallpaperPicker/res \ + $(LOCAL_PATH)/res \ + $(LOCAL_PATH)/../../../prebuilts/sdk/current/support/v7/recyclerview/res + LOCAL_PROGUARD_FLAG_FILES := proguard.flags -LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 android-support-v7-recyclerview LOCAL_PROTOC_OPTIMIZE_TYPE := nano LOCAL_PROTOC_FLAGS := --proto_path=$(LOCAL_PATH)/protos/ -LOCAL_AAPT_FLAGS := --auto-add-overlay +LOCAL_AAPT_FLAGS := \ + --auto-add-overlay \ + --extra-packages android.support.v7.recyclerview LOCAL_SDK_VERSION := current LOCAL_PACKAGE_NAME := Launcher3 From eea022530496bb5f53f9ae01fdef0cd9d0e8147d Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 27 Jul 2015 17:50:13 -0700 Subject: [PATCH 10/76] Fixing minor memory leak with name comparator. - WidgetsAndShortcutNameComparator was using the actual widget and shortcut resolve infos as the key to the label cache. Neither of these classes override hashCode() and we were retrieving a new set of widgets and shortcuts whenever packages changed so we would end up creating more and more entries in the cache. This isn't a huge leak, but could lead to problems if Launcher is used for long periods without being killed. - Now, we use a ComponentKey as the key, so that we don't keep a reference to the widget/shortcut infos and also ensures that they should hash to the same labels. Change-Id: I91347ee72363adbc2b075b67dba331e35ab1fe34 --- .../WidgetsAndShortcutNameComparator.java | 85 +++++++++++++------ .../android/launcher3/model/WidgetsModel.java | 5 +- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java index 61e8952831..b99056023a 100644 --- a/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java +++ b/src/com/android/launcher3/model/WidgetsAndShortcutNameComparator.java @@ -1,13 +1,14 @@ package com.android.launcher3.model; +import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; - import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.ComponentKey; import java.text.Collator; import java.util.Comparator; @@ -16,53 +17,81 @@ import java.util.HashMap; public class WidgetsAndShortcutNameComparator implements Comparator { private final AppWidgetManagerCompat mManager; private final PackageManager mPackageManager; - private final HashMap mLabelCache; + private final HashMap mLabelCache; private final Collator mCollator; private final UserHandleCompat mMainHandle; public WidgetsAndShortcutNameComparator(Context context) { mManager = AppWidgetManagerCompat.getInstance(context); mPackageManager = context.getPackageManager(); - mLabelCache = new HashMap(); + mLabelCache = new HashMap<>(); mCollator = Collator.getInstance(); mMainHandle = UserHandleCompat.myUserHandle(); } + /** + * Resets any stored state. + */ + public void reset() { + mLabelCache.clear(); + } + @Override - public final int compare(Object a, Object b) { - String labelA, labelB; - if (mLabelCache.containsKey(a)) { - labelA = mLabelCache.get(a); - } else { - labelA = (a instanceof LauncherAppWidgetProviderInfo) - ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) a)) - : Utilities.trim(((ResolveInfo) a).loadLabel(mPackageManager)); - mLabelCache.put(a, labelA); - } - if (mLabelCache.containsKey(b)) { - labelB = mLabelCache.get(b); - } else { - labelB = (b instanceof LauncherAppWidgetProviderInfo) - ? Utilities.trim(mManager.loadLabel((LauncherAppWidgetProviderInfo) b)) - : Utilities.trim(((ResolveInfo) b).loadLabel(mPackageManager)); - mLabelCache.put(b, labelB); - } - - // Currently, there is no work profile shortcuts, hence only considering the widget cases. - - boolean aWorkProfile = (a instanceof LauncherAppWidgetProviderInfo) && - !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) a)); - boolean bWorkProfile = (b instanceof LauncherAppWidgetProviderInfo) && - !mMainHandle.equals(mManager.getUser((LauncherAppWidgetProviderInfo) b)); + public final int compare(Object objA, Object objB) { + ComponentKey keyA = getComponentKey(objA); + ComponentKey keyB = getComponentKey(objB); // Independent of how the labels compare, if only one of the two widget info belongs to // work profile, put that one in the back. + boolean aWorkProfile = !mMainHandle.equals(keyA.user); + boolean bWorkProfile = !mMainHandle.equals(keyB.user); if (aWorkProfile && !bWorkProfile) { return 1; } if (!aWorkProfile && bWorkProfile) { return -1; } + + // Get the labels for comparison + String labelA = mLabelCache.get(keyA); + String labelB = mLabelCache.get(keyB); + if (labelA == null) { + labelA = getLabel(objA); + mLabelCache.put(keyA, labelA); + } + if (labelB == null) { + labelB = getLabel(objB); + mLabelCache.put(keyB, labelB); + } return mCollator.compare(labelA, labelB); } + + /** + * @return a component key for the given widget or shortcut info. + */ + private ComponentKey getComponentKey(Object o) { + if (o instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; + return new ComponentKey(widgetInfo.provider, mManager.getUser(widgetInfo)); + } else { + ResolveInfo shortcutInfo = (ResolveInfo) o; + ComponentName cn = new ComponentName(shortcutInfo.activityInfo.packageName, + shortcutInfo.activityInfo.name); + // Currently, there are no work profile shortcuts + return new ComponentKey(cn, UserHandleCompat.myUserHandle()); + } + } + + /** + * @return the label for the given widget or shortcut info. This may be an expensive call. + */ + private String getLabel(Object o) { + if (o instanceof LauncherAppWidgetProviderInfo) { + LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; + return Utilities.trim(mManager.loadLabel(widgetInfo)); + } else { + ResolveInfo shortcutInfo = (ResolveInfo) o; + return Utilities.trim(shortcutInfo.loadLabel(mPackageManager)); + } + } }; diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index 185dfcae3a..cabff148f9 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -39,7 +39,7 @@ public class WidgetsModel { private ArrayList mRawList; private final AppWidgetManagerCompat mAppWidgetMgr; - private final Comparator mWidgetAndShortcutNameComparator; + private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator; private final Comparator mAppNameComparator; private final IconCache mIconCache; private final AppFilter mAppFilter; @@ -103,6 +103,7 @@ public class WidgetsModel { // clear the lists. mWidgetsList.clear(); mPackageItemInfos.clear(); + mWidgetAndShortcutNameComparator.reset(); // add and update. for (Object o: rawWidgetsShortcuts) { @@ -139,7 +140,7 @@ public class WidgetsModel { if (widgetsShortcutsList != null) { widgetsShortcutsList.add(o); } else { - widgetsShortcutsList = new ArrayList(); + widgetsShortcutsList = new ArrayList<>(); widgetsShortcutsList.add(o); pInfo = new PackageItemInfo(packageName); mIconCache.getTitleAndIconForApp(packageName, userHandle, From 70d5a07c5db9414742b0fd357d8ceb45c9f924ff Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 30 Jul 2015 12:03:28 -0700 Subject: [PATCH 11/76] Prevent crash when flattening ComponentKey to string with invalid user. Bug: 22658306 Change-Id: I213d44e0bb22ac0617d46c80ab8cfec8f750a2cc --- src/com/android/launcher3/util/ComponentKey.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/util/ComponentKey.java b/src/com/android/launcher3/util/ComponentKey.java index 6a7df43186..b7aafaea9d 100644 --- a/src/com/android/launcher3/util/ComponentKey.java +++ b/src/com/android/launcher3/util/ComponentKey.java @@ -64,8 +64,11 @@ public class ComponentKey { * Encodes a component key as a string of the form [flattenedComponentString#userId]. */ public String flattenToString(Context context) { - return componentName.flattenToString() + "#" + - UserManagerCompat.getInstance(context).getSerialNumberForUser(user); + String flattened = componentName.flattenToString(); + if (user != null) { + flattened += "#" + UserManagerCompat.getInstance(context).getSerialNumberForUser(user); + } + return flattened; } @Override From deaf4d169b6e08ae2c41ae67ea97147164089b16 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Thu, 30 Jul 2015 12:53:33 -0700 Subject: [PATCH 12/76] Fixing issue with accessibility falsely reporting app count when there are no search results. Bug: 22672783 Change-Id: Ie40acc592e59699eac2a4ff2045a7f8ba029d33a --- .../launcher3/allapps/AllAppsGridAdapter.java | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index 057883cab4..e96567c413 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -22,12 +22,15 @@ import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; import android.os.Handler; +import android.support.v4.view.accessibility.AccessibilityRecordCompat; +import android.support.v4.view.accessibility.AccessibilityEventCompat; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; import com.android.launcher3.AppInfo; import com.android.launcher3.BubbleTextView; @@ -68,6 +71,38 @@ class AllAppsGridAdapter extends RecyclerView.Adapter Date: Thu, 30 Jul 2015 13:03:56 -0700 Subject: [PATCH 13/76] Remove references to FloatMath, deprecated in M -> Allows us to build against M SDK issue 22822360 Change-Id: I1178eb6bafc348699c5be8c29858eb3dff093e11 --- .../src/com/android/gallery3d/common/BitmapUtils.java | 7 +++---- WallpaperPicker/src/com/android/launcher3/CropView.java | 5 ++--- src/com/android/launcher3/LauncherScroller.java | 5 ++--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java index a671ed2b9d..38317eb41e 100644 --- a/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java +++ b/WallpaperPicker/src/com/android/gallery3d/common/BitmapUtils.java @@ -23,7 +23,6 @@ import android.graphics.Canvas; import android.graphics.Matrix; import android.graphics.Paint; import android.os.Build; -import android.util.FloatMath; import android.util.Log; import java.io.ByteArrayOutputStream; @@ -72,7 +71,7 @@ public class BitmapUtils { && minSideLength == UNCONSTRAINED) return 1; int lowerBound = (maxNumOfPixels == UNCONSTRAINED) ? 1 : - (int) FloatMath.ceil(FloatMath.sqrt((float) (w * h) / maxNumOfPixels)); + (int) Math.ceil(Math.sqrt((float) (w * h) / maxNumOfPixels)); if (minSideLength == UNCONSTRAINED) { return lowerBound; @@ -96,7 +95,7 @@ public class BitmapUtils { // Find the min x that 1 / x >= scale public static int computeSampleSizeLarger(float scale) { - int initialSize = (int) FloatMath.floor(1f / scale); + int initialSize = (int) Math.floor(1f / scale); if (initialSize <= 1) return 1; return initialSize <= 8 @@ -107,7 +106,7 @@ public class BitmapUtils { // Find the max x that 1 / x <= scale. public static int computeSampleSize(float scale) { Utils.assertTrue(scale > 0); - int initialSize = Math.max(1, (int) FloatMath.ceil(1 / scale)); + int initialSize = Math.max(1, (int) Math.ceil(1 / scale)); return initialSize <= 8 ? Utils.nextPowerOf2(initialSize) : (initialSize + 7) / 8 * 8; diff --git a/WallpaperPicker/src/com/android/launcher3/CropView.java b/WallpaperPicker/src/com/android/launcher3/CropView.java index 578b8eafd0..50f779add0 100644 --- a/WallpaperPicker/src/com/android/launcher3/CropView.java +++ b/WallpaperPicker/src/com/android/launcher3/CropView.java @@ -21,7 +21,6 @@ import android.graphics.Matrix; import android.graphics.Point; import android.graphics.RectF; import android.util.AttributeSet; -import android.util.FloatMath; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.ScaleGestureDetector.OnScaleGestureListener; @@ -300,12 +299,12 @@ public class CropView extends TiledImageView implements OnScaleGestureListener { adjustment[0] = (edges.right - getWidth()) / scale; } if (edges.top > 0) { - adjustment[1] = FloatMath.ceil(edges.top / scale); + adjustment[1] = (float) Math.ceil(edges.top / scale); } else if (edges.bottom < getHeight()) { adjustment[1] = (edges.bottom - getHeight()) / scale; } for (int dim = 0; dim <= 1; dim++) { - if (coef[dim] > 0) adjustment[dim] = FloatMath.ceil(adjustment[dim]); + if (coef[dim] > 0) adjustment[dim] = (float) Math.ceil(adjustment[dim]); } mInverseRotateMatrix.mapPoints(adjustment); diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java index 3bd0a78c47..bbc7e4cabf 100644 --- a/src/com/android/launcher3/LauncherScroller.java +++ b/src/com/android/launcher3/LauncherScroller.java @@ -20,7 +20,6 @@ import android.animation.TimeInterpolator; import android.content.Context; import android.hardware.SensorManager; import android.os.Build; -import android.util.FloatMath; import android.view.ViewConfiguration; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; @@ -409,7 +408,7 @@ public class LauncherScroller { float dx = (float) (mFinalX - mStartX); float dy = (float) (mFinalY - mStartY); - float hyp = FloatMath.sqrt(dx * dx + dy * dy); + float hyp = (float) Math.sqrt(dx * dx + dy * dy); float ndx = dx / hyp; float ndy = dy / hyp; @@ -426,7 +425,7 @@ public class LauncherScroller { mMode = FLING_MODE; mFinished = false; - float velocity = FloatMath.sqrt(velocityX * velocityX + velocityY * velocityY); + float velocity = (float) Math.sqrt(velocityX * velocityX + velocityY * velocityY); mVelocity = velocity; mDuration = getSplineFlingDuration(velocity); From b9f932e08902c21020b6a98372c1a68548b8de59 Mon Sep 17 00:00:00 2001 From: Hyunyoung Song Date: Thu, 30 Jul 2015 15:04:59 -0700 Subject: [PATCH 14/76] Prevent from widget cell preview image from being cut off. b/22541466 >TL;DR;; when introducing mProfileBadgeMargin to correctly place > work profile badge. Drawing origin of the preview image resulted > to the left of (0, 0). Change-Id: Ic52fc2e17c55c76f4f57aa833451ffa19bbbeb09 --- src/com/android/launcher3/WidgetPreviewLoader.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 629387ed0b..2a8053d527 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -386,7 +386,7 @@ public class WidgetPreviewLoader { preScaledWidthOut[0] = previewWidth; } if (previewWidth > maxPreviewWidth) { - scale = maxPreviewWidth / (float) previewWidth; + scale = (maxPreviewWidth - 2 * mProfileBadgeMargin) / (float) (previewWidth); } if (scale != 1f) { previewWidth = (int) (scale * previewWidth); @@ -405,7 +405,7 @@ public class WidgetPreviewLoader { } // Draw the scaled preview into the final bitmap - int x = (preview.getWidth() - previewWidth - mProfileBadgeMargin) / 2; + int x = (preview.getWidth() - previewWidth) / 2; if (widgetPreviewExists) { drawable.setBounds(x, 0, x + previewWidth, previewHeight); drawable.draw(c); From cb7674118c6fa6849e657fb8fea25c8fd93477cc Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 31 Jul 2015 10:58:44 -0700 Subject: [PATCH 15/76] Add proto flag "javanano_use_deprecated_package" to avoid build breakage Change-Id: Iea8a7bc2bb385e066b3f972105224fee3eb7c894 --- protos/backup.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/protos/backup.proto b/protos/backup.proto index f3ad0b61f2..e1af696d4c 100644 --- a/protos/backup.proto +++ b/protos/backup.proto @@ -18,6 +18,7 @@ syntax = "proto2"; package launcher_backup; +option javanano_use_deprecated_package = true; option java_package = "com.android.launcher3.backup"; option java_outer_classname = "BackupProtos"; From e5bb705fb79f18df8680958dcf2c5460e16c90b6 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 27 Jul 2015 14:36:07 -0700 Subject: [PATCH 16/76] Homescreen migration from a larger grid to a smaller grid. Adding support for restoring from a larger device, if the grid size difference is not more that 1. During restore add all the items in the DB, and run a one-time migration the next time launcher starts. The migration strategy is defined in ShrinkWorkspaceTask.java which involves resizing, moving and removing some items. Change-Id: I6ee411f6db5bf0152b527e16146a88c56dec2d97 --- .../android/launcher3/LauncherAppState.java | 3 +- .../LauncherAppWidgetProviderInfo.java | 12 + .../launcher3/LauncherBackupAgentHelper.java | 9 + .../launcher3/LauncherBackupHelper.java | 54 +- src/com/android/launcher3/LauncherModel.java | 22 +- .../android/launcher3/LauncherProvider.java | 2 +- .../android/launcher3/LauncherSettings.java | 2 +- .../model/MigrateFromRestoreTask.java | 765 ++++++++++++++++++ 8 files changed, 848 insertions(+), 21 deletions(-) create mode 100644 src/com/android/launcher3/model/MigrateFromRestoreTask.java diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 0b7b1fdc48..ecaf8016e2 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -17,7 +17,6 @@ package com.android.launcher3; import android.app.SearchManager; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; @@ -147,7 +146,7 @@ public class LauncherAppState { sLauncherProvider = new WeakReference(provider); } - static LauncherProvider getLauncherProvider() { + public static LauncherProvider getLauncherProvider() { return sLauncherProvider.get(); } diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index 85af92f30a..9ba78530d3 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -5,6 +5,7 @@ import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; +import android.graphics.Point; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Parcel; @@ -110,4 +111,15 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { mMinSpanY = minResizeSpan[1]; } } + + public Point getMinSpans(InvariantDeviceProfile idp, Context context) { + // Calculate the spans corresponding to any one of the orientations as it should not change + // based on orientation. + // TODO: Use the max of both profiles + int[] minSpans = CellLayout.rectToCell( + idp.portraitProfile, context, minResizeWidth, minResizeHeight, null); + return new Point( + (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpans[0] : -1, + (resizeMode & RESIZE_VERTICAL) != 0 ? minSpans[1] : -1); + } } diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index b7860d45bb..611ab2e928 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -24,6 +24,8 @@ import android.database.Cursor; import android.os.ParcelFileDescriptor; import android.util.Log; +import com.android.launcher3.model.MigrateFromRestoreTask; + import java.io.IOException; public class LauncherBackupAgentHelper extends BackupAgentHelper { @@ -96,6 +98,13 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { LauncherAppState.getLauncherProvider().updateFolderItemsRank(); } + if (mHelper.shouldAttemptWorkspaceMigration()) { + MigrateFromRestoreTask.markForMigration(getApplicationContext(), + (int) mHelper.migrationCompatibleProfileData.desktopCols, + (int) mHelper.migrationCompatibleProfileData.desktopRows, + mHelper.widgetSizes); + } + LauncherAppState.getLauncherProvider().convertShortcutsToLauncherActivities(); } else { if (VERBOSE) Log.v(TAG, "Nothing was restored, clearing DB"); diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 39603ad1bf..136556b927 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -32,6 +32,7 @@ import android.content.res.XmlResourceParser; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Point; import android.graphics.drawable.Drawable; import android.os.ParcelFileDescriptor; import android.text.TextUtils; @@ -152,6 +153,9 @@ public class LauncherBackupHelper implements BackupHelper { private DeviceProfieData mDeviceProfileData; private InvariantDeviceProfile mIdp; + DeviceProfieData migrationCompatibleProfileData; + HashSet widgetSizes = new HashSet<>(); + boolean restoreSuccessful; int restoredBackupVersion = 1; @@ -288,9 +292,9 @@ public class LauncherBackupHelper implements BackupHelper { return true; } - boolean isHotsetCompatible = false; + boolean isHotseatCompatible = false; if (currentProfile.allappsRank >= oldProfile.hotseatCount) { - isHotsetCompatible = true; + isHotseatCompatible = true; mHotseatShift = 0; } @@ -298,12 +302,28 @@ public class LauncherBackupHelper implements BackupHelper { && ((currentProfile.hotseatCount - currentProfile.allappsRank) >= (oldProfile.hotseatCount - oldProfile.allappsRank))) { // There is enough space on both sides of the hotseat. - isHotsetCompatible = true; + isHotseatCompatible = true; mHotseatShift = currentProfile.allappsRank - oldProfile.allappsRank; } - return isHotsetCompatible && (currentProfile.desktopCols >= oldProfile.desktopCols) - && (currentProfile.desktopRows >= oldProfile.desktopRows); + if (!isHotseatCompatible) { + return false; + } + if ((currentProfile.desktopCols >= oldProfile.desktopCols) + && (currentProfile.desktopRows >= oldProfile.desktopRows)) { + return true; + } + + if ((oldProfile.desktopCols - currentProfile.desktopCols <= 1) && + (oldProfile.desktopRows - currentProfile.desktopRows <= 1)) { + // Allow desktop migration when row and/or column count contracts by 1. + + migrationCompatibleProfileData = initDeviceProfileData(mIdp); + migrationCompatibleProfileData.desktopCols = oldProfile.desktopCols; + migrationCompatibleProfileData.desktopRows = oldProfile.desktopRows; + return true; + } + return false; } /** @@ -706,7 +726,8 @@ public class LauncherBackupHelper implements BackupHelper { } } - // future site of widget table mutation + // Cache widget min sizes incase migration is required. + widgetSizes.add(widget.provider + "#" + widget.minSpanX + "," + widget.minSpanY); } /** create a new key, with an integer ID. @@ -904,7 +925,11 @@ public class LauncherBackupHelper implements BackupHelper { UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle); values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber); - DeviceProfieData currentProfile = mDeviceProfileData; + // If we will attempt grid resize, use the original profile to validate grid size, as + // anything which fits in the original grid should fit in the current grid after + // grid migration. + DeviceProfieData currentProfile = migrationCompatibleProfileData == null + ? mDeviceProfileData : migrationCompatibleProfileData; if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) { if (!TextUtils.isEmpty(favorite.appWidgetProvider)) { @@ -996,14 +1021,9 @@ public class LauncherBackupHelper implements BackupHelper { widget.icon.dpi = dpi; } - // Calculate the spans corresponding to any one of the orientations as it should not change - // based on orientation. - int[] minSpans = CellLayout.rectToCell( - mIdp.portraitProfile, mContext, info.minResizeWidth, info.minResizeHeight, null); - widget.minSpanX = (info.resizeMode & LauncherAppWidgetProviderInfo.RESIZE_HORIZONTAL) != 0 - ? minSpans[0] : -1; - widget.minSpanY = (info.resizeMode & LauncherAppWidgetProviderInfo.RESIZE_VERTICAL) != 0 - ? minSpans[1] : -1; + Point spans = info.getMinSpans(mIdp, mContext); + widget.minSpanX = spans.x; + widget.minSpanY = spans.y; return widget; } @@ -1188,6 +1208,10 @@ public class LauncherBackupHelper implements BackupHelper { } } + public boolean shouldAttemptWorkspaceMigration() { + return migrationCompatibleProfileData != null; + } + /** * A class to check if an activity can handle one of the intents from a list of * predefined intents. diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 0b67310fa9..7e75f97936 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -27,6 +27,7 @@ import android.content.Context; import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; +import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -55,6 +56,7 @@ import com.android.launcher3.compat.PackageInstallerCompat; import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.model.MigrateFromRestoreTask; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.CursorIconInfo; @@ -1133,7 +1135,7 @@ public class LauncherModel extends BroadcastReceiver * Update the order of the workspace screens in the database. The array list contains * a list of screen ids in the order that they should appear. */ - void updateWorkspaceScreenOrder(Context context, final ArrayList screens) { + public void updateWorkspaceScreenOrder(Context context, final ArrayList screens) { final ArrayList screensCopy = new ArrayList(screens); final ContentResolver cr = context.getContentResolver(); final Uri uri = LauncherSettings.WorkspaceScreens.CONTENT_URI; @@ -1411,7 +1413,7 @@ public class LauncherModel extends BroadcastReceiver /** * Loads the workspace screen ids in an ordered list. */ - @Thunk static ArrayList loadWorkspaceScreensDb(Context context) { + public static ArrayList loadWorkspaceScreensDb(Context context) { final ContentResolver contentResolver = context.getContentResolver(); final Uri screensUri = LauncherSettings.WorkspaceScreens.CONTENT_URI; @@ -1757,6 +1759,22 @@ public class LauncherModel extends BroadcastReceiver int countX = (int) profile.numColumns; int countY = (int) profile.numRows; + + if (MigrateFromRestoreTask.shouldRunTask(mContext)) { + try { + MigrateFromRestoreTask task = new MigrateFromRestoreTask(mContext); + // Clear the flags before starting the task, so that we do not run the task + // again, in case there was an uncaught error. + MigrateFromRestoreTask.clearFlags(mContext); + task.execute(); + } catch (Exception e) { + Log.e(TAG, "Error during grid migration", e); + + // Clear workspace. + mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE; + } + } + if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) { Launcher.addDumpLog(TAG, "loadWorkspace: resetting launcher database", true); LauncherAppState.getLauncherProvider().deleteDatabase(); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index cc5e18bc17..6cbb267bed 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -71,7 +71,7 @@ public class LauncherProvider extends ContentProvider { private static final int DATABASE_VERSION = 26; static final String OLD_AUTHORITY = "com.android.launcher2.settings"; - static final String AUTHORITY = ProviderConfig.AUTHORITY; + public static final String AUTHORITY = ProviderConfig.AUTHORITY; static final String TABLE_FAVORITES = LauncherSettings.Favorites.TABLE_NAME; static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME; diff --git a/src/com/android/launcher3/LauncherSettings.java b/src/com/android/launcher3/LauncherSettings.java index f2c85a1950..8a5804f349 100644 --- a/src/com/android/launcher3/LauncherSettings.java +++ b/src/com/android/launcher3/LauncherSettings.java @@ -143,7 +143,7 @@ public class LauncherSettings { * * @return The unique content URL for the specified row. */ - static Uri getContentUri(long id) { + public static Uri getContentUri(long id) { return Uri.parse("content://" + ProviderConfig.AUTHORITY + "/" + TABLE_NAME + "/" + id); } diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java new file mode 100644 index 0000000000..cb90c8bc9d --- /dev/null +++ b/src/com/android/launcher3/model/MigrateFromRestoreTask.java @@ -0,0 +1,765 @@ +package com.android.launcher3.model; + +import android.content.ComponentName; +import android.content.ContentProviderOperation; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.pm.PackageInfo; +import android.database.Cursor; +import android.graphics.Point; +import android.text.TextUtils; +import android.util.Log; + +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.LauncherModel; +import com.android.launcher3.LauncherProvider; +import com.android.launcher3.LauncherSettings; +import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.util.LongArrayMap; +import com.android.launcher3.util.Thunk; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; + +/** + * This class takes care of shrinking the workspace (by maximum of one row and one column), as a + * result of restoring from a larger device. + */ +public class MigrateFromRestoreTask { + + private static final String TAG = "MigrateFromRestoreTask"; + private static final boolean DEBUG = false; + + private static final String KEY_MIGRATION_SOURCE_SIZE = "migration_restore_src_size"; + private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size"; + + // These are carefully selected weights for various item types (Math.random?), to allow for + // the lease absurd migration experience. + private static final float WT_SHORTCUT = 1; + private static final float WT_APPLICATION = 0.8f; + private static final float WT_WIDGET_MIN = 2; + private static final float WT_WIDGET_FACTOR = 0.6f; + private static final float WT_FOLDER_FACTOR = 0.5f; + + private final Context mContext; + private final ContentValues mTempValues = new ContentValues(); + private final HashMap mWidgetMinSize; + private final InvariantDeviceProfile mIdp; + + private HashSet mValidPackages; + public ArrayList mEntryToRemove; + private ArrayList mUpdateOperations; + + private ArrayList mCarryOver; + + private final int mSrcX, mSrcY; + @Thunk final int mTrgX, mTrgY; + private final boolean mShouldRemoveX, mShouldRemoveY; + + public MigrateFromRestoreTask(Context context) { + mContext = context; + + SharedPreferences prefs = prefs(context); + Point sourceSize = parsePoint(prefs.getString(KEY_MIGRATION_SOURCE_SIZE, "")); + mSrcX = sourceSize.x; + mSrcY = sourceSize.y; + + mWidgetMinSize = new HashMap(); + for (String s : prefs.getStringSet(KEY_MIGRATION_WIDGET_MINSIZE, + Collections.emptySet())) { + String[] parts = s.split("#"); + mWidgetMinSize.put(parts[0], parsePoint(parts[1])); + } + + mIdp = LauncherAppState.getInstance().getInvariantDeviceProfile(); + mTrgX = mIdp.numColumns; + mTrgY = mIdp.numRows; + mShouldRemoveX = mTrgX < mSrcX; + mShouldRemoveY = mTrgY < mSrcY; + } + + public void execute() throws Exception { + mEntryToRemove = new ArrayList<>(); + mCarryOver = new ArrayList<>(); + mUpdateOperations = new ArrayList<>(); + + // Initialize list of valid packages. This contain all the packages which are already on + // the device and packages which are being installed. Any item which doesn't belong to + // this set is removed. + // Since the loader removes such items anyway, removing these items here doesn't cause any + // extra data loss and gives us more free space on the grid for better migration. + mValidPackages = new HashSet<>(); + for (PackageInfo info : mContext.getPackageManager().getInstalledPackages(0)) { + mValidPackages.add(info.packageName); + } + mValidPackages.addAll(PackageInstallerCompat.getInstance(mContext) + .updateAndGetActiveSessionCache().keySet()); + + ArrayList allScreens = LauncherModel.loadWorkspaceScreensDb(mContext); + if (allScreens.isEmpty()) { + throw new Exception("Unable to get workspace screens"); + } + + for (long screenId : allScreens) { + if (DEBUG) { + Log.d(TAG, "Migrating " + screenId); + } + migrateScreen(screenId); + } + + if (!mCarryOver.isEmpty()) { + LongArrayMap itemMap = new LongArrayMap<>(); + for (DbEntry e : mCarryOver) { + itemMap.put(e.id, e); + } + + do { + // Some items are still remaining. Try adding a few new screens. + + // At every iteration, make sure that at least one item is removed from + // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed, + // break the loop and abort migration by throwing an exception. + OptimalPlacementSolution placement = new OptimalPlacementSolution( + new boolean[mTrgX][mTrgY], deepCopy(mCarryOver), true); + placement.find(); + if (placement.finalPlacedItems.size() > 0) { + long newScreenId = LauncherAppState.getLauncherProvider().generateNewScreenId(); + allScreens.add(newScreenId); + for (DbEntry item : placement.finalPlacedItems) { + if (!mCarryOver.remove(itemMap.get(item.id))) { + throw new Exception("Unable to find matching items"); + } + item.screenId = newScreenId; + update(item); + } + } else { + throw new Exception("None of the items can be placed on an empty screen"); + } + + } while (!mCarryOver.isEmpty()); + + + LauncherAppState.getInstance().getModel() + .updateWorkspaceScreenOrder(mContext, allScreens); + } + + // Update items + mContext.getContentResolver().applyBatch(LauncherProvider.AUTHORITY, mUpdateOperations); + + if (!mEntryToRemove.isEmpty()) { + if (DEBUG) { + Log.d(TAG, "Removing items: " + TextUtils.join(", ", mEntryToRemove)); + } + mContext.getContentResolver().delete(LauncherSettings.Favorites.CONTENT_URI, + Utilities.createDbSelectionQuery( + LauncherSettings.Favorites._ID, mEntryToRemove), null); + } + + // Make sure we haven't removed everything. + final Cursor c = mContext.getContentResolver().query( + LauncherSettings.Favorites.CONTENT_URI, null, null, null, null); + boolean hasData = c.moveToNext(); + c.close(); + if (!hasData) { + throw new Exception("Removed every thing during grid resize"); + } + } + + /** + * Migrate a particular screen id. + * Strategy: + * 1) For all possible combinations of row and column, pick the one which causes the least + * data loss: {@link #tryRemove(int, int, ArrayList, float[])} + * 2) Maintain a list of all lost items before this screen, and add any new item lost from + * this screen to that list as well. + * 3) If all those items from the above list can be placed on this screen, place them + * (otherwise they are placed on a new screen). + */ + private void migrateScreen(long screenId) { + ArrayList items = loadEntries(screenId); + + int removedCol = Integer.MAX_VALUE; + int removedRow = Integer.MAX_VALUE; + + // removeWt represents the cost function for loss of items during migration, and moveWt + // represents the cost function for repositioning the items. moveWt is only considered if + // removeWt is same for two different configurations. + // Start with Float.MAX_VALUE (assuming full data) and pick the configuration with least + // cost. + float removeWt = Float.MAX_VALUE; + float moveWt = Float.MAX_VALUE; + float[] outLoss = new float[2]; + ArrayList finalItems = null; + + // Try removing all possible combinations + for (int x = 0; x < mSrcX; x++) { + for (int y = 0; y < mSrcY; y++) { + // Use a deep copy when trying out a particular combination as it can change + // the underlying object. + ArrayList itemsOnScreen = tryRemove(x, y, deepCopy(items), outLoss); + + if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) { + removeWt = outLoss[0]; + moveWt = outLoss[1]; + removedCol = mShouldRemoveX ? x : removedCol; + removedRow = mShouldRemoveY ? y : removedRow; + finalItems = itemsOnScreen; + } + + // No need to loop over all rows, if a row removal is not needed. + if (!mShouldRemoveY) { + break; + } + } + + if (!mShouldRemoveX) { + break; + } + } + + if (DEBUG) { + Log.d(TAG, String.format("Removing row %d, column %d on screen %d", + removedRow, removedCol, screenId)); + } + + LongArrayMap itemMap = new LongArrayMap<>(); + for (DbEntry e : deepCopy(items)) { + itemMap.put(e.id, e); + } + + for (DbEntry item : finalItems) { + DbEntry org = itemMap.get(item.id); + itemMap.remove(item.id); + + // Check if update is required + if (!item.columnsSame(org)) { + update(item); + } + } + + // The remaining items in {@link #itemMap} are those which didn't get placed. + for (DbEntry item : itemMap) { + mCarryOver.add(item); + } + + if (!mCarryOver.isEmpty() && removeWt == 0) { + // No new items were removed in this step. Try placing all the items on this screen. + boolean[][] occupied = new boolean[mTrgX][mTrgY]; + for (DbEntry item : finalItems) { + markCells(occupied, item, true); + } + + OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, + deepCopy(mCarryOver), true); + placement.find(); + if (placement.lowestWeightLoss == 0) { + // All items got placed + + for (DbEntry item : placement.finalPlacedItems) { + item.screenId = screenId; + update(item); + } + + mCarryOver.clear(); + } + } + } + + /** + * Updates an item in the DB. + */ + private void update(DbEntry item) { + mTempValues.clear(); + item.addToContentValues(mTempValues); + mUpdateOperations.add(ContentProviderOperation + .newUpdate(LauncherSettings.Favorites.getContentUri(item.id)) + .withValues(mTempValues).build()); + } + + /** + * Tries the remove the provided row and column. + * @param items all the items on the screen under operation + * @param outLoss array of size 2. The first entry is filled with weight loss, and the second + * with the overall item movement. + */ + private ArrayList tryRemove(int col, int row, ArrayList items, + float[] outLoss) { + boolean[][] occupied = new boolean[mTrgX][mTrgY]; + + col = mShouldRemoveX ? col : Integer.MAX_VALUE; + row = mShouldRemoveY ? row : Integer.MAX_VALUE; + + ArrayList finalItems = new ArrayList<>(); + ArrayList removedItems = new ArrayList<>(); + + for (DbEntry item : items) { + if ((item.cellX <= col && (item.spanX + item.cellX) > col) + || (item.cellY <= row && (item.spanY + item.cellY) > row)) { + removedItems.add(item); + if (item.cellX >= col) item.cellX --; + if (item.cellY >= row) item.cellY --; + } else { + if (item.cellX > col) item.cellX --; + if (item.cellY > row) item.cellY --; + finalItems.add(item); + markCells(occupied, item, true); + } + } + + OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems); + placement.find(); + finalItems.addAll(placement.finalPlacedItems); + outLoss[0] = placement.lowestWeightLoss; + outLoss[1] = placement.lowestMoveCost; + return finalItems; + } + + @Thunk void markCells(boolean[][] occupied, DbEntry item, boolean val) { + for (int i = item.cellX; i < (item.cellX + item.spanX); i++) { + for (int j = item.cellY; j < (item.cellY + item.spanY); j++) { + occupied[i][j] = val; + } + } + } + + @Thunk boolean isVacant(boolean[][] occupied, int x, int y, int w, int h) { + if (x + w > mTrgX) return false; + if (y + h > mTrgY) return false; + + for (int i = 0; i < w; i++) { + for (int j = 0; j < h; j++) { + if (occupied[i + x][j + y]) { + return false; + } + } + } + return true; + } + + private class OptimalPlacementSolution { + private final ArrayList itemsToPlace; + private final boolean[][] occupied; + + // If set to true, item movement are not considered in move cost, leading to a more + // linear placement. + private final boolean ignoreMove; + + float lowestWeightLoss = Float.MAX_VALUE; + float lowestMoveCost = Float.MAX_VALUE; + ArrayList finalPlacedItems; + + public OptimalPlacementSolution(boolean[][] occupied, ArrayList itemsToPlace) { + this(occupied, itemsToPlace, false); + } + + public OptimalPlacementSolution(boolean[][] occupied, ArrayList itemsToPlace, + boolean ignoreMove) { + this.occupied = occupied; + this.itemsToPlace = itemsToPlace; + this.ignoreMove = ignoreMove; + + // Sort the items such that larger widgets appear first followed by 1x1 items + Collections.sort(this.itemsToPlace); + } + + public void find() { + find(0, 0, 0, new ArrayList()); + } + + /** + * Recursively finds a placement for the provided items. + * @param index the position in {@link #itemsToPlace} to start looking at. + * @param weightLoss total weight loss upto this point + * @param moveCost total move cost upto this point + * @param itemsPlaced all the items already placed upto this point + */ + public void find(int index, float weightLoss, float moveCost, + ArrayList itemsPlaced) { + if ((weightLoss >= lowestWeightLoss) || + ((weightLoss == lowestWeightLoss) && (moveCost >= lowestMoveCost))) { + // Abort, as we already have a better solution. + return; + + } else if (index >= itemsToPlace.size()) { + // End loop. + lowestWeightLoss = weightLoss; + lowestMoveCost = moveCost; + + // Keep a deep copy of current configuration as it can change during recursion. + finalPlacedItems = deepCopy(itemsPlaced); + return; + } + + DbEntry me = itemsToPlace.get(index); + int myX = me.cellX; + int myY = me.cellY; + + // List of items to pass over if this item was placed. + ArrayList itemsIncludingMe = new ArrayList<>(itemsPlaced.size() + 1); + itemsIncludingMe.addAll(itemsPlaced); + itemsIncludingMe.add(me); + + if (me.spanX > 1 || me.spanY > 1) { + // If the current item is a widget (and it greater than 1x1), try to place it at + // all possible positions. This is because a widget placed at one position can + // affect the placement of a different widget. + int myW = me.spanX; + int myH = me.spanY; + + for (int y = 0; y < mTrgY; y++) { + for (int x = 0; x < mTrgX; x++) { + float newMoveCost = moveCost; + if (x != myX) { + me.cellX = x; + newMoveCost ++; + } + if (y != myY) { + me.cellY = y; + newMoveCost ++; + } + if (ignoreMove) { + newMoveCost = moveCost; + } + + if (isVacant(occupied, x, y, myW, myH)) { + // place at this position and continue search. + markCells(occupied, me, true); + find(index + 1, weightLoss, newMoveCost, itemsIncludingMe); + markCells(occupied, me, false); + } + + // Try resizing horizontally + if (myW > me.minSpanX && isVacant(occupied, x, y, myW - 1, myH)) { + me.spanX --; + markCells(occupied, me, true); + // 1 extra move cost + find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe); + markCells(occupied, me, false); + me.spanX ++; + } + + // Try resizing vertically + if (myH > me.minSpanY && isVacant(occupied, x, y, myW, myH - 1)) { + me.spanY --; + markCells(occupied, me, true); + // 1 extra move cost + find(index + 1, weightLoss, newMoveCost + 1, itemsIncludingMe); + markCells(occupied, me, false); + me.spanY ++; + } + + // Try resizing horizontally & vertically + if (myH > me.minSpanY && myW > me.minSpanX && + isVacant(occupied, x, y, myW - 1, myH - 1)) { + me.spanX --; + me.spanY --; + markCells(occupied, me, true); + // 2 extra move cost + find(index + 1, weightLoss, newMoveCost + 2, itemsIncludingMe); + markCells(occupied, me, false); + me.spanX ++; + me.spanY ++; + } + me.cellX = myX; + me.cellY = myY; + } + } + + // Finally also try a solution when this item is not included. Trying it in the end + // causes it to get skipped in most cases due to higher weight loss, and prevents + // unnecessary deep copies of various configurations. + find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced); + } else { + // Since this is a 1x1 item and all the following items are also 1x1, just place + // it at 'the most appropriate position' and hope for the best. + // The most appropriate position: one with lease straight line distance + int newDistance = Integer.MAX_VALUE; + int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE; + + for (int y = 0; y < mTrgY; y++) { + for (int x = 0; x < mTrgX; x++) { + if (!occupied[x][y]) { + int dist = ignoreMove ? 0 : + ((me.cellX - x) * (me.cellX - x) + (me.cellY - y) * (me.cellY - y)); + if (dist < newDistance) { + newX = x; + newY = y; + newDistance = dist; + } + } + } + } + + if (newX < mTrgX && newY < mTrgY) { + float newMoveCost = moveCost; + if (newX != myX) { + me.cellX = newX; + newMoveCost ++; + } + if (newY != myY) { + me.cellY = newY; + newMoveCost ++; + } + if (ignoreMove) { + newMoveCost = moveCost; + } + markCells(occupied, me, true); + find(index + 1, weightLoss, newMoveCost, itemsIncludingMe); + markCells(occupied, me, false); + me.cellX = myX; + me.cellY = myY; + + // Try to find a solution without this item, only if + // 1) there was at least one space, i.e., we were able to place this item + // 2) if the next item has the same weight (all items are already sorted), as + // if it has lower weight, that solution will automatically get discarded. + // 3) ignoreMove false otherwise, move cost is ignored and the weight will + // anyway be same. + if (index + 1 < itemsToPlace.size() + && itemsToPlace.get(index + 1).weight >= me.weight && !ignoreMove) { + find(index + 1, weightLoss + me.weight, moveCost, itemsPlaced); + } + } else { + // No more space. Jump to the end. + for (int i = index + 1; i < itemsToPlace.size(); i++) { + weightLoss += itemsToPlace.get(i).weight; + } + find(itemsToPlace.size(), weightLoss + me.weight, moveCost, itemsPlaced); + } + } + } + } + + /** + * Loads entries for a particular screen id. + */ + public ArrayList loadEntries(long screen) { + Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[] { + Favorites._ID, // 0 + Favorites.ITEM_TYPE, // 1 + Favorites.CELLX, // 2 + Favorites.CELLY, // 3 + Favorites.SPANX, // 4 + Favorites.SPANY, // 5 + Favorites.INTENT, // 6 + Favorites.APPWIDGET_PROVIDER}, // 7 + Favorites.CONTAINER + " = " + Favorites.CONTAINER_DESKTOP + + " AND " + Favorites.SCREEN + " = " + screen, null, null, null); + + final int indexId = c.getColumnIndexOrThrow(Favorites._ID); + final int indexItemType = c.getColumnIndexOrThrow(Favorites.ITEM_TYPE); + final int indexCellX = c.getColumnIndexOrThrow(Favorites.CELLX); + final int indexCellY = c.getColumnIndexOrThrow(Favorites.CELLY); + final int indexSpanX = c.getColumnIndexOrThrow(Favorites.SPANX); + final int indexSpanY = c.getColumnIndexOrThrow(Favorites.SPANY); + final int indexIntent = c.getColumnIndexOrThrow(Favorites.INTENT); + final int indexAppWidgetProvider = c.getColumnIndexOrThrow(Favorites.APPWIDGET_PROVIDER); + + ArrayList entries = new ArrayList<>(); + while (c.moveToNext()) { + DbEntry entry = new DbEntry(); + entry.id = c.getLong(indexId); + entry.itemType = c.getInt(indexItemType); + entry.cellX = c.getInt(indexCellX); + entry.cellY = c.getInt(indexCellY); + entry.spanX = c.getInt(indexSpanX); + entry.spanY = c.getInt(indexSpanY); + entry.screenId = screen; + + try { + // calculate weight + switch (entry.itemType) { + case Favorites.ITEM_TYPE_SHORTCUT: + case Favorites.ITEM_TYPE_APPLICATION: { + verifyIntent(c.getString(indexIntent)); + entry.weight = entry.itemType == Favorites.ITEM_TYPE_SHORTCUT + ? WT_SHORTCUT : WT_APPLICATION; + break; + } + case Favorites.ITEM_TYPE_APPWIDGET: { + String provider = c.getString(indexAppWidgetProvider); + ComponentName cn = ComponentName.unflattenFromString(provider); + verifyPackage(cn.getPackageName()); + entry.weight = Math.max(WT_WIDGET_MIN, WT_WIDGET_FACTOR + * entry.spanX * entry.spanY); + + // Migration happens for current user only. + LauncherAppWidgetProviderInfo pInfo = LauncherModel.getProviderInfo( + mContext, cn, UserHandleCompat.myUserHandle()); + Point spans = pInfo == null ? + mWidgetMinSize.get(provider) : pInfo.getMinSpans(mIdp, mContext); + if (spans != null) { + entry.minSpanX = spans.x > 0 ? spans.x : entry.spanX; + entry.minSpanY = spans.y > 0 ? spans.y : entry.spanY; + } else { + // Assume that the widget be resized down to 2x2 + entry.minSpanX = entry.minSpanY = 2; + } + + if (entry.minSpanX > mTrgX || entry.minSpanY > mTrgY) { + throw new Exception("Widget can't be resized down to fit the grid"); + } + break; + } + case Favorites.ITEM_TYPE_FOLDER: { + int total = getFolderItemsCount(entry.id); + if (total == 0) { + throw new Exception("Folder is empty"); + } + entry.weight = WT_FOLDER_FACTOR * total; + break; + } + default: + throw new Exception("Invalid item type"); + } + } catch (Exception e) { + if (DEBUG) { + Log.d(TAG, "Removing item " + entry.id, e); + } + mEntryToRemove.add(entry.id); + continue; + } + + entries.add(entry); + } + return entries; + } + + /** + * @return the number of valid items in the folder. + */ + private int getFolderItemsCount(long folderId) { + Cursor c = mContext.getContentResolver().query(LauncherSettings.Favorites.CONTENT_URI, + new String[] {Favorites._ID, Favorites.INTENT}, + Favorites.CONTAINER + " = " + folderId, null, null, null); + + int total = 0; + while (c.moveToNext()) { + try { + verifyIntent(c.getString(1)); + total++; + } catch (Exception e) { + mEntryToRemove.add(c.getLong(0)); + } + } + + return total; + } + + /** + * Verifies if the intent should be restored. + */ + private void verifyIntent(String intentStr) throws Exception { + Intent intent = Intent.parseUri(intentStr, 0); + if (intent.getComponent() != null) { + verifyPackage(intent.getComponent().getPackageName()); + } else if (intent.getPackage() != null) { + // Only verify package if the component was null. + verifyPackage(intent.getPackage()); + } + } + + /** + * Verifies if the package should be restored + */ + private void verifyPackage(String packageName) throws Exception { + if (!mValidPackages.contains(packageName)) { + throw new Exception("Package not available"); + } + } + + private static class DbEntry extends ItemInfo implements Comparable { + + public float weight; + + public DbEntry() { } + + public DbEntry copy() { + DbEntry entry = new DbEntry(); + entry.copyFrom(this); + entry.weight = weight; + entry.minSpanX = minSpanX; + entry.minSpanY = minSpanY; + return entry; + } + + /** + * Comparator such that larger widgets come first, followed by all 1x1 items + * based on their weights. + */ + @Override + public int compareTo(DbEntry another) { + if (itemType == Favorites.ITEM_TYPE_APPWIDGET) { + if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) { + return another.spanY * another.spanX - spanX * spanY; + } else { + return -1; + } + } else if (another.itemType == Favorites.ITEM_TYPE_APPWIDGET) { + return 1; + } else { + // Place higher weight before lower weight. + return Float.compare(another.weight, weight); + } + } + + public boolean columnsSame(DbEntry org) { + return org.cellX == cellX && org.cellY == cellY && org.spanX == spanX && + org.spanY == spanY && org.screenId == screenId; + } + + public void addToContentValues(ContentValues values) { + values.put(LauncherSettings.Favorites.SCREEN, screenId); + values.put(LauncherSettings.Favorites.CELLX, cellX); + values.put(LauncherSettings.Favorites.CELLY, cellY); + values.put(LauncherSettings.Favorites.SPANX, spanX); + values.put(LauncherSettings.Favorites.SPANY, spanY); + } + } + + @Thunk static ArrayList deepCopy(ArrayList src) { + ArrayList dup = new ArrayList(src.size()); + for (DbEntry e : src) { + dup.add(e.copy()); + } + return dup; + } + + private static Point parsePoint(String point) { + String[] split = point.split(","); + return new Point(Integer.parseInt(split[0]), Integer.parseInt(split[1])); + } + + public static void markForMigration(Context context, int srcX, int srcY, + HashSet widgets) { + prefs(context).edit() + .putString(KEY_MIGRATION_SOURCE_SIZE, srcX + "," + srcY) + .putStringSet(KEY_MIGRATION_WIDGET_MINSIZE, widgets) + .apply(); + } + + public static boolean shouldRunTask(Context context) { + return !TextUtils.isEmpty(prefs(context).getString(KEY_MIGRATION_SOURCE_SIZE, "")); + } + + public static void clearFlags(Context context) { + prefs(context).edit().remove(KEY_MIGRATION_SOURCE_SIZE) + .remove(KEY_MIGRATION_WIDGET_MINSIZE).commit(); + } + + private static SharedPreferences prefs(Context context) { + return context.getSharedPreferences(LauncherAppState.getSharedPreferencesKey(), + Context.MODE_PRIVATE); + } +} From e8f1d047b1b788bc9884be2417e7aa0cec7e8ae8 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 31 Jul 2015 12:39:57 -0700 Subject: [PATCH 17/76] Fixing issue with flash during overview transition. - This is due to the TransitionDrawable which does not actually start an animation until it is first drawn. This workaround will just immediately reset the transition if it is not currently visible instead of animating (which is actually deferred until the next animation into overview mode). Bug: 22414257 Change-Id: Id481303d0c99a20c1d16396c024ab50303f45576 --- src/com/android/launcher3/CellLayout.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 8096887128..b875d22e62 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -211,6 +211,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { mBackground = (TransitionDrawable) res.getDrawable(R.drawable.bg_screenpanel); mBackground.setCallback(this); + mBackground.setAlpha((int) (mBackgroundAlpha * 255)); mReorderPreviewAnimationMagnitude = (REORDER_PREVIEW_MAGNITUDE * grid.iconSizePx); @@ -414,7 +415,11 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { if (mIsDragOverlapping) { mBackground.startTransition(BACKGROUND_ACTIVATE_DURATION); } else { - mBackground.reverseTransition(BACKGROUND_ACTIVATE_DURATION); + if (mBackgroundAlpha > 0f) { + mBackground.reverseTransition(BACKGROUND_ACTIVATE_DURATION); + } else { + mBackground.resetTransition(); + } } invalidate(); } From d934e0b0b7b60c9457fd0eb615355c16bac1a285 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 31 Jul 2015 12:40:57 -0700 Subject: [PATCH 18/76] Enabling logging for migration task Bug: 22353460 Change-Id: I8a9a8ea0d5523acb6d2376920535a9f4f519525d --- src/com/android/launcher3/LauncherModel.java | 5 ++++- src/com/android/launcher3/model/MigrateFromRestoreTask.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 7e75f97936..41f36eb04f 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -27,7 +27,6 @@ import android.content.Context; import android.content.Intent; import android.content.Intent.ShortcutIconResource; import android.content.IntentFilter; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ResolveInfo; @@ -1761,6 +1760,8 @@ public class LauncherModel extends BroadcastReceiver if (MigrateFromRestoreTask.shouldRunTask(mContext)) { + long migrationStartTime = System.currentTimeMillis(); + Log.v(TAG, "Starting workspace migration after restore"); try { MigrateFromRestoreTask task = new MigrateFromRestoreTask(mContext); // Clear the flags before starting the task, so that we do not run the task @@ -1773,6 +1774,8 @@ public class LauncherModel extends BroadcastReceiver // Clear workspace. mFlags = mFlags | LOADER_FLAG_CLEAR_WORKSPACE; } + Log.v(TAG, "Workspace migration completed in " + + (System.currentTimeMillis() - migrationStartTime)); } if ((mFlags & LOADER_FLAG_CLEAR_WORKSPACE) != 0) { diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java index cb90c8bc9d..8d4472fd18 100644 --- a/src/com/android/launcher3/model/MigrateFromRestoreTask.java +++ b/src/com/android/launcher3/model/MigrateFromRestoreTask.java @@ -38,7 +38,7 @@ import java.util.HashSet; public class MigrateFromRestoreTask { private static final String TAG = "MigrateFromRestoreTask"; - private static final boolean DEBUG = false; + private static final boolean DEBUG = true; private static final String KEY_MIGRATION_SOURCE_SIZE = "migration_restore_src_size"; private static final String KEY_MIGRATION_WIDGET_MINSIZE = "migration_widget_min_size"; From a547c4516c1a2dfd87f3466fccffd5ca6c5cab8c Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Fri, 31 Jul 2015 21:06:10 +0000 Subject: [PATCH 19/76] Revert "Add proto flag "javanano_use_deprecated_package" to avoid build breakage" This reverts commit cb7674118c6fa6849e657fb8fea25c8fd93477cc. Change-Id: I831abe3746be49fd164046655e8c5e059bae074f --- protos/backup.proto | 1 - 1 file changed, 1 deletion(-) diff --git a/protos/backup.proto b/protos/backup.proto index e1af696d4c..f3ad0b61f2 100644 --- a/protos/backup.proto +++ b/protos/backup.proto @@ -18,7 +18,6 @@ syntax = "proto2"; package launcher_backup; -option javanano_use_deprecated_package = true; option java_package = "com.android.launcher3.backup"; option java_outer_classname = "BackupProtos"; From 6eeab68fd4a5ba51e32717b18c40e9bea8b38e97 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 16 Jul 2015 12:21:00 -0700 Subject: [PATCH 20/76] Fixing drag outline positions for folder over hotseat Bug: 22506247 Change-Id: Ia2508307554089205fd678987454e81cb811a59b (cherry picked from commit 92bce4316422c69ee26e6ece204fbf1499753086) --- src/com/android/launcher3/Workspace.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 267787be87..086934773f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2276,6 +2276,8 @@ public class Workspace extends PagedView dragRect = new Rect(left, top, right, bottom); } else if (child instanceof FolderIcon) { int previewSize = grid.folderIconSizePx; + dragVisualizeOffset = new Point(-padding.get() / 2, + padding.get() / 2 - child.getPaddingTop()); dragRect = new Rect(0, child.getPaddingTop(), child.getWidth(), previewSize); } From 88fa741e50760f9da78cba4a766172f23773bb87 Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 3 Aug 2015 14:25:28 -0700 Subject: [PATCH 21/76] Remove handler for searchables changed broadcast. Bug: 22515084 --- src/com/android/launcher3/Launcher.java | 2 +- src/com/android/launcher3/LauncherAppState.java | 1 - src/com/android/launcher3/LauncherModel.java | 7 +++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 6648b6e74d..15e43b2b90 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -4101,7 +4101,7 @@ public class Launcher extends Activity return mDeviceProfile.getSearchBarBounds(Utilities.isRtl(getResources())); } - public void bindSearchablesChanged() { + public void bindSearchProviderChanged() { if (mSearchDropTargetBar == null) { return; } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 0b7b1fdc48..0d087c22de 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -96,7 +96,6 @@ public class LauncherAppState { IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); - filter.addAction(SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED); // For handling managed profiles filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED); filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 0b67310fa9..b7fb60a788 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -203,7 +203,7 @@ public class LauncherModel extends BroadcastReceiver public void bindComponentsRemoved(ArrayList packageNames, ArrayList appInfos, UserHandleCompat user, int reason); public void bindAllPackages(WidgetsModel model); - public void bindSearchablesChanged(); + public void bindSearchProviderChanged(); public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); public void dumpLogsToLocalData(); @@ -1280,11 +1280,10 @@ public class LauncherModel extends BroadcastReceiver if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); - } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action) || - SearchManager.INTENT_ACTION_SEARCHABLES_CHANGED.equals(action)) { + } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) { Callbacks callbacks = getCallback(); if (callbacks != null) { - callbacks.bindSearchablesChanged(); + callbacks.bindSearchProviderChanged(); } } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action) || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { From 233ee964a9ecf419a3e8330a67456d422879132d Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 3 Aug 2015 13:05:01 -0700 Subject: [PATCH 22/76] Calculating widget minSpans and spans statically/independent of orientation > Filtering the widget list and excluding widgets which dont fit the grid > setting minSpans for the widget item when binding. Bug: 22541314 Bug: 22559137 Change-Id: Ieda48b56c95bee0c7ec71dd691af7e23e2d43db6 --- .../launcher3/AppWidgetResizeFrame.java | 4 +- src/com/android/launcher3/CellLayout.java | 60 ------------- src/com/android/launcher3/Launcher.java | 21 +---- .../launcher3/LauncherAppWidgetInfo.java | 4 - .../LauncherAppWidgetProviderInfo.java | 87 ++++++++++--------- .../launcher3/WidgetPreviewLoader.java | 4 +- .../allapps/AllAppsContainerView.java | 1 - .../android/launcher3/model/WidgetsModel.java | 28 +++++- .../widget/PendingAddWidgetInfo.java | 16 +--- .../android/launcher3/widget/WidgetCell.java | 4 +- .../widget/WidgetsContainerView.java | 1 - 11 files changed, 81 insertions(+), 149 deletions(-) diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index ea7c22189e..e6bf525315 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -75,8 +75,8 @@ public class AppWidgetResizeFrame extends FrameLayout { mResizeMode = info.resizeMode; mDragLayer = dragLayer; - mMinHSpan = info.getMinSpanX(mLauncher); - mMinVSpan = info.getMinSpanY(mLauncher); + mMinHSpan = info.minSpanX; + mMinVSpan = info.minSpanY; setBackgroundResource(R.drawable.widget_resize_shadow); setForeground(getResources().getDrawable(R.drawable.widget_resize_frame)); diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index b875d22e62..84e2d49c23 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -54,7 +54,6 @@ import com.android.launcher3.accessibility.DragAndDropAccessibilityDelegate; import com.android.launcher3.accessibility.FolderAccessibilityHelper; import com.android.launcher3.accessibility.WorkspaceAccessibilityHelper; import com.android.launcher3.util.Thunk; -import com.android.launcher3.widget.PendingAddWidgetInfo; import java.util.ArrayList; import java.util.Arrays; @@ -2686,65 +2685,6 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { resultRect.set(x, y, x + width, y + height); } - /** - * Computes the required horizontal and vertical cell spans to always - * fit the given rectangle. - * - * @param width Width in pixels - * @param height Height in pixels - * @param result An array of length 2 in which to store the result (may be null). - */ - public static int[] rectToCell(Launcher launcher, int width, int height, int[] result) { - return rectToCell(launcher.getDeviceProfile(), launcher, width, height, result); - } - - public static int[] rectToCell(DeviceProfile grid, Context context, int width, int height, - int[] result) { - Rect padding = grid.getWorkspacePadding(Utilities.isRtl(context.getResources())); - - // Always assume we're working with the smallest span to make sure we - // reserve enough space in both orientations. - int parentWidth = DeviceProfile.calculateCellWidth(grid.widthPx - - padding.left - padding.right, (int) grid.inv.numColumns); - int parentHeight = DeviceProfile.calculateCellHeight(grid.heightPx - - padding.top - padding.bottom, (int) grid.inv.numRows); - int smallerSize = Math.min(parentWidth, parentHeight); - - // Always round up to next largest cell - int spanX = (int) Math.ceil(width / (float) smallerSize); - int spanY = (int) Math.ceil(height / (float) smallerSize); - - if (result == null) { - return new int[] { spanX, spanY }; - } - result[0] = spanX; - result[1] = spanY; - return result; - } - - /** - * Calculate the grid spans needed to fit given item - */ - public void calculateSpans(ItemInfo info) { - final int minWidth; - final int minHeight; - - if (info instanceof LauncherAppWidgetInfo) { - minWidth = ((LauncherAppWidgetInfo) info).minWidth; - minHeight = ((LauncherAppWidgetInfo) info).minHeight; - } else if (info instanceof PendingAddWidgetInfo) { - minWidth = ((PendingAddWidgetInfo) info).minWidth; - minHeight = ((PendingAddWidgetInfo) info).minHeight; - } else { - // It's not a widget, so it must be 1x1 - info.spanX = info.spanY = 1; - return; - } - int[] spans = rectToCell(mLauncher, minWidth, minHeight, null); - info.spanX = spans[0]; - info.spanY = spans[1]; - } - private void clearOccupiedCells() { for (int x = 0; x < mCountX; x++) { for (int y = 0; y < mCountY; y++) { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 6648b6e74d..a6c7289873 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1553,23 +1553,6 @@ public class Launcher extends Activity } } - private int[] getSpanForWidget(ComponentName component, int minWidth, int minHeight) { - Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(this, component, null); - // We want to account for the extra amount of padding that we are adding to the widget - // to ensure that it gets the full amount of space that it has requested - int requiredWidth = minWidth + padding.left + padding.right; - int requiredHeight = minHeight + padding.top + padding.bottom; - return CellLayout.rectToCell(this, requiredWidth, requiredHeight, null); - } - - public int[] getSpanForWidget(AppWidgetProviderInfo info) { - return getSpanForWidget(info.provider, info.minWidth, info.minHeight); - } - - public int[] getMinSpanForWidget(AppWidgetProviderInfo info) { - return getSpanForWidget(info.provider, info.minResizeWidth, info.minResizeHeight); - } - /** * Add a widget to the workspace. * @@ -2218,7 +2201,7 @@ public class Launcher extends Activity mPendingAddInfo.screenId = -1; mPendingAddInfo.cellX = mPendingAddInfo.cellY = -1; mPendingAddInfo.spanX = mPendingAddInfo.spanY = -1; - mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = -1; + mPendingAddInfo.minSpanX = mPendingAddInfo.minSpanY = 1; mPendingAddInfo.dropPos = null; } @@ -3957,6 +3940,8 @@ public class Launcher extends Activity } item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo); + item.minSpanX = appWidgetInfo.minSpanX; + item.minSpanY = appWidgetInfo.minSpanY; } else { appWidgetInfo = null; PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item, diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index aad18b578e..882f7e2026 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -68,10 +68,6 @@ public class LauncherAppWidgetInfo extends ItemInfo { ComponentName providerName; - // TODO: Are these necessary here? - int minWidth = -1; - int minHeight = -1; - /** * Indicates the restore status of the widget. */ diff --git a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java index 9ba78530d3..71a08a8536 100644 --- a/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetProviderInfo.java @@ -1,11 +1,13 @@ package com.android.launcher3; import android.annotation.TargetApi; +import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetProviderInfo; import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Parcel; @@ -20,10 +22,10 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { public boolean isCustomWidget = false; - private int mSpanX = -1; - private int mSpanY = -1; - private int mMinSpanX = -1; - private int mMinSpanY = -1; + public int spanX; + public int spanY; + public int minSpanX; + public int minSpanY; public static LauncherAppWidgetProviderInfo fromProviderInfo(Context context, AppWidgetProviderInfo info) { @@ -42,6 +44,7 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { public LauncherAppWidgetProviderInfo(Parcel in) { super(in); + initSpans(); } public LauncherAppWidgetProviderInfo(Context context, CustomAppWidget widget) { @@ -53,6 +56,41 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { previewImage = widget.getPreviewImage(); initialLayout = widget.getWidgetLayout(); resizeMode = widget.getResizeMode(); + initSpans(); + } + + private void initSpans() { + LauncherAppState app = LauncherAppState.getInstance(); + InvariantDeviceProfile idp = app.getInvariantDeviceProfile(); + + // We only care out the cell size, which is independent of the the layout direction. + Rect paddingLand = idp.landscapeProfile.getWorkspacePadding(false /* isLayoutRtl */); + Rect paddingPort = idp.portraitProfile.getWorkspacePadding(false /* isLayoutRtl */); + + // Always assume we're working with the smallest span to make sure we + // reserve enough space in both orientations. + float smallestCellWidth = DeviceProfile.calculateCellWidth(Math.min( + idp.landscapeProfile.widthPx - paddingLand.left - paddingLand.right, + idp.portraitProfile.widthPx - paddingPort.left - paddingPort.right), + idp.numColumns); + float smallestCellHeight = DeviceProfile.calculateCellWidth(Math.min( + idp.landscapeProfile.heightPx - paddingLand.top - paddingLand.bottom, + idp.portraitProfile.heightPx - paddingPort.top - paddingPort.bottom), + idp.numRows); + + // We want to account for the extra amount of padding that we are adding to the widget + // to ensure that it gets the full amount of space that it has requested. + Rect widgetPadding = AppWidgetHostView.getDefaultPaddingForWidget( + app.getContext(), provider, null); + spanX = Math.max(1, (int) Math.ceil( + (minWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth)); + spanY = Math.max(1, (int) Math.ceil( + (minHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight)); + + minSpanX = Math.max(1, (int) Math.ceil( + (minResizeWidth + widgetPadding.left + widgetPadding.right) / smallestCellWidth)); + minSpanY = Math.max(1, (int) Math.ceil( + (minResizeHeight + widgetPadding.top + widgetPadding.bottom) / smallestCellHeight)); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @@ -80,46 +118,9 @@ public class LauncherAppWidgetProviderInfo extends AppWidgetProviderInfo { provider.toString(), provider.getPackageName(), provider.getShortClassName(), getLabel(pm)); } - public int getSpanX(Launcher launcher) { - lazyLoadSpans(launcher); - return mSpanX; - } - - public int getSpanY(Launcher launcher) { - lazyLoadSpans(launcher); - return mSpanY; - } - - public int getMinSpanX(Launcher launcher) { - lazyLoadSpans(launcher); - return mMinSpanX; - } - - public int getMinSpanY(Launcher launcher) { - lazyLoadSpans(launcher); - return mMinSpanY; - } - - private void lazyLoadSpans(Launcher launcher) { - if (mSpanX < 0 || mSpanY < 0 || mMinSpanX < 0 || mMinSpanY < 0) { - int[] minResizeSpan = launcher.getMinSpanForWidget(this); - int[] span = launcher.getSpanForWidget(this); - - mSpanX = span[0]; - mSpanY = span[1]; - mMinSpanX = minResizeSpan[0]; - mMinSpanY = minResizeSpan[1]; - } - } - public Point getMinSpans(InvariantDeviceProfile idp, Context context) { - // Calculate the spans corresponding to any one of the orientations as it should not change - // based on orientation. - // TODO: Use the max of both profiles - int[] minSpans = CellLayout.rectToCell( - idp.portraitProfile, context, minResizeWidth, minResizeHeight, null); return new Point( - (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpans[0] : -1, - (resizeMode & RESIZE_VERTICAL) != 0 ? minSpans[1] : -1); + (resizeMode & RESIZE_HORIZONTAL) != 0 ? minSpanX : -1, + (resizeMode & RESIZE_VERTICAL) != 0 ? minSpanY : -1); } } diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 2a8053d527..1fb795addb 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -361,8 +361,8 @@ public class WidgetPreviewLoader { } final boolean widgetPreviewExists = (drawable != null); - final int spanX = info.getSpanX(launcher) < 1 ? 1 : info.getSpanX(launcher); - final int spanY = info.getSpanY(launcher) < 1 ? 1 : info.getSpanY(launcher); + final int spanX = info.spanX; + final int spanY = info.spanY; int previewWidth; int previewHeight; diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 67d572819a..010b2cb486 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -528,7 +528,6 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); ItemInfo itemInfo = (ItemInfo) d.dragInfo; if (layout != null) { - layout.calculateSpans(itemInfo); showOutOfSpaceMessage = !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); } diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java index cabff148f9..eef4f9173a 100644 --- a/src/com/android/launcher3/model/WidgetsModel.java +++ b/src/com/android/launcher3/model/WidgetsModel.java @@ -8,6 +8,9 @@ import android.util.Log; import com.android.launcher3.AppFilter; import com.android.launcher3.IconCache; +import com.android.launcher3.InvariantDeviceProfile; +import com.android.launcher3.ItemInfo; +import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppWidgetProviderInfo; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AlphabeticIndexCompat; @@ -40,7 +43,7 @@ public class WidgetsModel { private final AppWidgetManagerCompat mAppWidgetMgr; private final WidgetsAndShortcutNameComparator mWidgetAndShortcutNameComparator; - private final Comparator mAppNameComparator; + private final Comparator mAppNameComparator; private final IconCache mIconCache; private final AppFilter mAppFilter; private AlphabeticIndexCompat mIndexer; @@ -54,6 +57,7 @@ public class WidgetsModel { mIndexer = new AlphabeticIndexCompat(context); } + @SuppressWarnings("unchecked") private WidgetsModel(WidgetsModel model) { mAppWidgetMgr = model.mAppWidgetMgr; mPackageItemInfos = (ArrayList) model.mPackageItemInfos.clone(); @@ -105,6 +109,8 @@ public class WidgetsModel { mPackageItemInfos.clear(); mWidgetAndShortcutNameComparator.reset(); + InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile(); + // add and update. for (Object o: rawWidgetsShortcuts) { String packageName = ""; @@ -112,9 +118,23 @@ public class WidgetsModel { ComponentName componentName = null; if (o instanceof LauncherAppWidgetProviderInfo) { LauncherAppWidgetProviderInfo widgetInfo = (LauncherAppWidgetProviderInfo) o; - componentName = widgetInfo.provider; - packageName = widgetInfo.provider.getPackageName(); - userHandle = mAppWidgetMgr.getUser(widgetInfo); + + // Ensure that all widgets we show can be added on a workspace of this size + int minSpanX = Math.min(widgetInfo.spanX, widgetInfo.minSpanX); + int minSpanY = Math.min(widgetInfo.spanY, widgetInfo.minSpanY); + if (minSpanX <= (int) idp.numColumns && + minSpanY <= (int) idp.numRows) { + componentName = widgetInfo.provider; + packageName = widgetInfo.provider.getPackageName(); + userHandle = mAppWidgetMgr.getUser(widgetInfo); + } else { + if (DEBUG) { + Log.d(TAG, String.format( + "Widget %s : (%d X %d) can't fit on this device", + widgetInfo.provider, minSpanX, minSpanY)); + } + continue; + } } else if (o instanceof ResolveInfo) { ResolveInfo resolveInfo = (ResolveInfo) o; componentName = new ComponentName(resolveInfo.activityInfo.packageName, diff --git a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java index 758287af3a..fcb714ff19 100644 --- a/src/com/android/launcher3/widget/PendingAddWidgetInfo.java +++ b/src/com/android/launcher3/widget/PendingAddWidgetInfo.java @@ -31,10 +31,6 @@ import com.android.launcher3.compat.AppWidgetManagerCompat; * @see {@link PendingAddItemInfo} */ public class PendingAddWidgetInfo extends PendingAddItemInfo { - public int minWidth; - public int minHeight; - public int minResizeWidth; - public int minResizeHeight; public int previewImage; public int icon; public LauncherAppWidgetProviderInfo info; @@ -50,17 +46,13 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo { this.info = i; user = AppWidgetManagerCompat.getInstance(launcher).getUser(i); componentName = i.provider; - minWidth = i.minWidth; - minHeight = i.minHeight; - minResizeWidth = i.minResizeWidth; - minResizeHeight = i.minResizeHeight; previewImage = i.previewImage; icon = i.icon; - spanX = i.getSpanX(launcher); - spanY = i.getSpanY(launcher); - minSpanX = i.getMinSpanX(launcher); - minSpanY = i.getMinSpanY(launcher); + spanX = i.spanX; + spanY = i.spanY; + minSpanX = i.minSpanX; + minSpanY = i.minSpanY; } public boolean isCustomWidget() { diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java index 7496ea2eff..94bbd929fb 100644 --- a/src/com/android/launcher3/widget/WidgetCell.java +++ b/src/com/android/launcher3/widget/WidgetCell.java @@ -146,8 +146,8 @@ public class WidgetCell extends LinearLayout implements OnLayoutChangeListener { mInfo = info; // TODO(hyunyoungs): setup a cache for these labels. mWidgetName.setText(AppWidgetManagerCompat.getInstance(getContext()).loadLabel(info)); - int hSpan = Math.min(info.getSpanX(mLauncher), profile.numColumns); - int vSpan = Math.min(info.getSpanY(mLauncher), profile.numRows); + int hSpan = Math.min(info.spanX, profile.numColumns); + int vSpan = Math.min(info.spanY, profile.numRows); mWidgetDims.setText(String.format(mDimensionsFormatString, hSpan, vSpan)); mWidgetPreviewLoader = loader; } diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 5afd7c4933..0c6ea31bbb 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -319,7 +319,6 @@ public class WidgetsContainerView extends BaseContainerView CellLayout layout = (CellLayout) workspace.getChildAt(currentScreen); ItemInfo itemInfo = (ItemInfo) d.dragInfo; if (layout != null) { - layout.calculateSpans(itemInfo); showOutOfSpaceMessage = !layout.findCellForSpan(null, itemInfo.spanX, itemInfo.spanY); } From 209a859dedba0e8314ee102a760aad78a814ea94 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Mon, 3 Aug 2015 16:59:10 -0700 Subject: [PATCH 23/76] Fix issue where scroll container shadow didn't fade out Change-Id: I3b0104dea04009819db0375c381b8ff445b76baa --- .../launcher3/WallpaperPickerActivity.java | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 88dc3e22b7..c723b39be0 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -100,6 +100,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Thunk LinearLayout mWallpapersView; @Thunk HorizontalScrollView mWallpaperScrollContainer; + @Thunk View mWallpaperStrip; @Thunk ActionMode.Callback mActionModeCallback; @Thunk ActionMode mActionMode; @@ -379,6 +380,7 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { mProgressView = findViewById(R.id.loading); mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); + mWallpaperStrip = findViewById(R.id.wallpaper_strip); mCropView.setTouchCallback(new CropView.TouchCallback() { ViewPropertyAnimator mAnim; @Override @@ -386,15 +388,15 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { if (mAnim != null) { mAnim.cancel(); } - if (mWallpaperScrollContainer.getAlpha() == 1f) { + if (mWallpaperStrip.getAlpha() == 1f) { mIgnoreNextTap = true; } - mAnim = mWallpaperScrollContainer.animate(); + mAnim = mWallpaperStrip.animate(); mAnim.alpha(0f) .setDuration(150) .withEndAction(new Runnable() { public void run() { - mWallpaperScrollContainer.setVisibility(View.INVISIBLE); + mWallpaperStrip.setVisibility(View.INVISIBLE); } }); mAnim.setInterpolator(new AccelerateInterpolator(0.75f)); @@ -412,8 +414,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { if (mAnim != null) { mAnim.cancel(); } - mWallpaperScrollContainer.setVisibility(View.VISIBLE); - mAnim = mWallpaperScrollContainer.animate(); + mWallpaperStrip.setVisibility(View.VISIBLE); + mAnim = mWallpaperStrip.animate(); mAnim.alpha(1f) .setDuration(150) .setInterpolator(new DecelerateInterpolator(0.75f)); @@ -713,10 +715,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { public void onStop() { super.onStop(); - mWallpaperScrollContainer = (HorizontalScrollView) findViewById(R.id.wallpaper_scroll_container); - if (mWallpaperScrollContainer.getAlpha() < 1f) { - mWallpaperScrollContainer.setAlpha(1f); - mWallpaperScrollContainer.setVisibility(View.VISIBLE); + mWallpaperStrip = findViewById(R.id.wallpaper_strip); + if (mWallpaperStrip.getAlpha() < 1f) { + mWallpaperStrip.setAlpha(1f); + mWallpaperStrip.setVisibility(View.VISIBLE); } } From 912bdfcffa04f8917ff56cc4e059208c13cbe29c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 3 Aug 2015 19:09:55 -0700 Subject: [PATCH 24/76] Replacing exception with a log, when uninstalling an app leads to an illegal state Bug: 22491055 Change-Id: Iaf5fe20b717102bdb0a5442dcc33efea30c50d47 --- src/com/android/launcher3/Workspace.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 086934773f..662eabc7c1 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -3768,7 +3768,11 @@ public class Workspace extends PagedView if (parentCell != null) { parentCell.removeView(v); } else if (LauncherAppState.isDogfoodBuild()) { - throw new NullPointerException("mDragInfo.cell has null parent"); + // When an app is uninstalled using the drop target, we wait until resume to remove + // the icon. We also remove all the corresponding items from the workspace at + // {@link Launcher#bindComponentsRemoved}. That call can come before or after + // {@link Launcher#mOnResumeCallbacks} depending on how busy the worker thread is. + Log.e(TAG, "mDragInfo.cell has null parent"); } if (v instanceof DropTarget) { mDragController.removeDropTarget((DropTarget) v); From 317698bd012c1930b83bb4e2e47ac9e363fa6c6a Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 29 Jul 2015 11:45:41 -0700 Subject: [PATCH 25/76] Loading high resolution icons for first 3 items in a folder > Sometimes a folder gets rearranged, without updating the model, like when an app in uninstalled. In that case, we need to update the icons for folder items, which were previously hidden Bug: 22813360 Change-Id: I99754911c969bf2153efb2948c226c1c69219b88 --- src/com/android/launcher3/BubbleTextView.java | 9 +++++++++ src/com/android/launcher3/Folder.java | 2 +- src/com/android/launcher3/FolderPagedView.java | 4 ++++ src/com/android/launcher3/Launcher.java | 1 + src/com/android/launcher3/LauncherModel.java | 15 +++++++++++++++ src/com/android/launcher3/ShortcutInfo.java | 8 ++++++-- src/com/android/launcher3/Workspace.java | 10 ++++++++++ 7 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index a0be8ea2b2..1bcaab5194 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -34,11 +34,13 @@ import android.util.SparseArray; import android.util.TypedValue; import android.view.KeyEvent; import android.view.MotionEvent; +import android.view.View; import android.view.ViewConfiguration; import android.view.ViewParent; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.TextView; + import com.android.launcher3.IconCache.IconLoadRequest; import com.android.launcher3.model.PackageItemInfo; @@ -538,6 +540,13 @@ public class BubbleTextView extends TextView } else if (info instanceof ShortcutInfo) { applyFromShortcutInfo((ShortcutInfo) info, LauncherAppState.getInstance().getIconCache()); + if ((info.rank < FolderIcon.NUM_ITEMS_IN_PREVIEW) && (info.container >= 0)) { + View folderIcon = + mLauncher.getWorkspace().getHomescreenIconByItemId(info.container); + if (folderIcon != null) { + folderIcon.invalidate(); + } + } } else if (info instanceof PackageItemInfo) { applyFromPackageItemInfo((PackageItemInfo) info); } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 2e19f6eba2..f4fffbe3e4 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -1389,7 +1389,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList } // Compares item position based on rank and position giving priority to the rank. - private static final Comparator ITEM_POS_COMPARATOR = new Comparator() { + public static final Comparator ITEM_POS_COMPARATOR = new Comparator() { @Override public int compare(ItemInfo lhs, ItemInfo rhs) { diff --git a/src/com/android/launcher3/FolderPagedView.java b/src/com/android/launcher3/FolderPagedView.java index f2ec1b68c5..cc9c5738aa 100644 --- a/src/com/android/launcher3/FolderPagedView.java +++ b/src/com/android/launcher3/FolderPagedView.java @@ -329,6 +329,10 @@ public class FolderPagedView extends PagedView { lp.cellY = info.cellY; currentPage.addViewToCellLayout( v, -1, mFolder.mLauncher.getViewIdForItem(info), lp, true); + + if (rank < FolderIcon.NUM_ITEMS_IN_PREVIEW && v instanceof BubbleTextView) { + ((BubbleTextView) v).verifyHighRes(); + } } rank ++; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8b27960489..3b9ebb6d31 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2662,6 +2662,7 @@ public class Launcher extends Activity throw new IllegalArgumentException("Input must be a FolderIcon"); } + // TODO(sunnygoyal): Re-evaluate this code. FolderIcon folderIcon = (FolderIcon) v; final FolderInfo info = folderIcon.getFolderInfo(); Folder openFolder = mWorkspace.getFolderForTag(info); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 2e66d34f69..c6fa8acf67 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2303,6 +2303,21 @@ public class LauncherModel extends BroadcastReceiver } } + // Sort all the folder items and make sure the first 3 items are high resolution. + for (FolderInfo folder : sBgFolders) { + Collections.sort(folder.contents, Folder.ITEM_POS_COMPARATOR); + int pos = 0; + for (ShortcutInfo info : folder.contents) { + if (info.usingLowResIcon) { + info.updateIcon(mIconCache, false); + } + pos ++; + if (pos >= FolderIcon.NUM_ITEMS_IN_PREVIEW) { + break; + } + } + } + if (restoredRows.size() > 0) { // Update restored items that no longer require special handling ContentValues values = new ContentValues(); diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 56c0b9d2fc..5766cf2f20 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -198,13 +198,17 @@ public class ShortcutInfo extends ItemInfo { return mIcon; } - public void updateIcon(IconCache iconCache) { + public void updateIcon(IconCache iconCache, boolean useLowRes) { if (itemType == Favorites.ITEM_TYPE_APPLICATION) { iconCache.getTitleAndIcon(this, promisedIntent != null ? promisedIntent : intent, user, - shouldUseLowResIcon()); + useLowRes); } } + public void updateIcon(IconCache iconCache) { + updateIcon(iconCache, shouldUseLowResIcon()); + } + @Override void onAddToDatabase(Context context, ContentValues values) { super.onAddToDatabase(context, values); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 662eabc7c1..4bd24ef7aa 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4059,6 +4059,16 @@ public class Workspace extends PagedView }); } + public View getHomescreenIconByItemId(final long id) { + return getFirstMatch(new ItemOperator() { + + @Override + public boolean evaluate(ItemInfo info, View v, View parent) { + return info.id == id; + } + }); + } + public View getViewForTag(final Object tag) { return getFirstMatch(new ItemOperator() { From 823fd5090209017a029460e7dbd8ab9d51d013dd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 4 Aug 2015 11:40:13 -0700 Subject: [PATCH 26/76] Creating a cache of users to avoid multiple calls to UserManager Bug: 22805101 Change-Id: I9cd270efd74fdd34a7eb738fc25797e6f1bf2487 --- .../android/launcher3/LauncherAppState.java | 3 +++ src/com/android/launcher3/LauncherModel.java | 1 + .../launcher3/compat/UserManagerCompat.java | 25 ++++++++++++----- .../compat/UserManagerCompatV16.java | 4 +++ .../compat/UserManagerCompatV17.java | 24 +++++++++++++++++ .../launcher3/compat/UserManagerCompatVL.java | 27 +++++++++++++++++++ .../model/AbstractUserComparator.java | 22 ++------------- .../launcher3/model/AppNameComparator.java | 2 -- 8 files changed, 80 insertions(+), 28 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index b285a683cc..52f85ea428 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -20,11 +20,13 @@ import android.app.SearchManager; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.os.UserManager; import android.util.Log; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.PackageInstallerCompat; +import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.util.Thunk; import java.lang.ref.WeakReference; @@ -100,6 +102,7 @@ public class LauncherAppState { filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED); sContext.registerReceiver(mModel, filter); + UserManagerCompat.getInstance(sContext).enableAndResetCache(); } /** diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index c6fa8acf67..f029561a32 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1288,6 +1288,7 @@ public class LauncherModel extends BroadcastReceiver } } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action) || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { + UserManagerCompat.getInstance(context).enableAndResetCache(); forceReload(); } } diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java index a79d94646d..2ff1e7b749 100644 --- a/src/com/android/launcher3/compat/UserManagerCompat.java +++ b/src/com/android/launcher3/compat/UserManagerCompat.java @@ -28,16 +28,29 @@ public abstract class UserManagerCompat { protected UserManagerCompat() { } + private static final Object sInstanceLock = new Object(); + private static UserManagerCompat sInstance; + public static UserManagerCompat getInstance(Context context) { - if (Utilities.isLmpOrAbove()) { - return new UserManagerCompatVL(context); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { - return new UserManagerCompatV17(context); - } else { - return new UserManagerCompatV16(); + synchronized (sInstanceLock) { + if (sInstance == null) { + if (Utilities.isLmpOrAbove()) { + sInstance = new UserManagerCompatVL(context.getApplicationContext()); + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + sInstance = new UserManagerCompatV17(context.getApplicationContext()); + } else { + sInstance = new UserManagerCompatV16(); + } + } + return sInstance; } } + /** + * Creates a cache for users. + */ + public abstract void enableAndResetCache(); + public abstract List getUserProfiles(); public abstract long getSerialNumberForUser(UserHandleCompat user); public abstract UserHandleCompat getUserForSerialNumber(long serialNumber); diff --git a/src/com/android/launcher3/compat/UserManagerCompatV16.java b/src/com/android/launcher3/compat/UserManagerCompatV16.java index ffe698c8b5..85aee57e85 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatV16.java +++ b/src/com/android/launcher3/compat/UserManagerCompatV16.java @@ -53,4 +53,8 @@ public class UserManagerCompatV16 extends UserManagerCompat { public long getUserCreationTime(UserHandleCompat user) { return 0; } + + @Override + public void enableAndResetCache() { + } } diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java index c42c00c7d3..1687569f34 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatV17.java +++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java @@ -21,8 +21,12 @@ import android.content.Context; import android.os.Build; import android.os.UserManager; +import com.android.launcher3.util.LongArrayMap; + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public class UserManagerCompatV17 extends UserManagerCompatV16 { + + protected LongArrayMap mUsers; protected UserManager mUserManager; UserManagerCompatV17(Context context) { @@ -30,11 +34,31 @@ public class UserManagerCompatV17 extends UserManagerCompatV16 { } public long getSerialNumberForUser(UserHandleCompat user) { + synchronized (this) { + if (mUsers != null) { + int index = mUsers.indexOfValue(user); + return (index >= 0) ? mUsers.keyAt(index) : 0; + } + } return mUserManager.getSerialNumberForUser(user.getUser()); } public UserHandleCompat getUserForSerialNumber(long serialNumber) { + synchronized (this) { + if (mUsers != null) { + return mUsers.get(serialNumber); + } + } return UserHandleCompat.fromUser(mUserManager.getUserForSerialNumber(serialNumber)); } + + @Override + public void enableAndResetCache() { + synchronized (this) { + mUsers = new LongArrayMap(); + UserHandleCompat myUser = UserHandleCompat.myUserHandle(); + mUsers.put(mUserManager.getSerialNumberForUser(myUser.getUser()), myUser); + } + } } diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java index dd7a726175..04cd0fdb41 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVL.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -24,7 +24,10 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.UserHandle; + import com.android.launcher3.LauncherAppState; +import com.android.launcher3.util.LongArrayMap; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -42,8 +45,32 @@ public class UserManagerCompatVL extends UserManagerCompatV17 { mContext = context; } + @Override + public void enableAndResetCache() { + synchronized (this) { + mUsers = new LongArrayMap(); + List users = mUserManager.getUserProfiles(); + if (users != null) { + for (UserHandle user : users) { + mUsers.put(mUserManager.getSerialNumberForUser(user), + UserHandleCompat.fromUser(user)); + } + } + } + } + @Override public List getUserProfiles() { + synchronized (this) { + if (mUsers != null) { + List users = new ArrayList<>(); + for (UserHandleCompat user : mUsers) { + users.add(user); + } + return users; + } + } + List users = mUserManager.getUserProfiles(); if (users == null) { return Collections.emptyList(); diff --git a/src/com/android/launcher3/model/AbstractUserComparator.java b/src/com/android/launcher3/model/AbstractUserComparator.java index cf47ce648d..bd28560f3c 100644 --- a/src/com/android/launcher3/model/AbstractUserComparator.java +++ b/src/com/android/launcher3/model/AbstractUserComparator.java @@ -22,14 +22,12 @@ import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import java.util.Comparator; -import java.util.HashMap; /** * A comparator to arrange items based on user profiles. */ public abstract class AbstractUserComparator implements Comparator { - private HashMap mUserSerialCache = new HashMap<>(); private final UserManagerCompat mUserManager; private final UserHandleCompat mMyUser; @@ -43,25 +41,9 @@ public abstract class AbstractUserComparator implements Comp if (mMyUser.equals(lhs.user)) { return -1; } else { - Long aUserSerial = getAndCacheUserSerial(lhs.user); - Long bUserSerial = getAndCacheUserSerial(rhs.user); + Long aUserSerial = mUserManager.getSerialNumberForUser(lhs.user); + Long bUserSerial = mUserManager.getSerialNumberForUser(rhs.user); return aUserSerial.compareTo(bUserSerial); } } - - /** - * Returns the user serial for this user, using a cached serial if possible. - */ - private Long getAndCacheUserSerial(UserHandleCompat user) { - Long userSerial = mUserSerialCache.get(user); - if (userSerial == null) { - userSerial = mUserManager.getSerialNumberForUser(user); - mUserSerialCache.put(user, userSerial); - } - return userSerial; - } - - public void clearUserCache() { - mUserSerialCache.clear(); - } } diff --git a/src/com/android/launcher3/model/AppNameComparator.java b/src/com/android/launcher3/model/AppNameComparator.java index c4b74d4dc4..5f80037dc8 100644 --- a/src/com/android/launcher3/model/AppNameComparator.java +++ b/src/com/android/launcher3/model/AppNameComparator.java @@ -68,8 +68,6 @@ public class AppNameComparator { * Returns a locale-aware comparator that will alphabetically order a list of applications. */ public Comparator getAppInfoComparator() { - // Clear the user serial cache so that we get serials as needed in the comparator - mAppInfoComparator.clearUserCache(); return mAppInfoComparator; } From c679ed623324954069446f50e9c4ace359fa55e7 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 4 Aug 2015 15:09:58 -0700 Subject: [PATCH 27/76] Removing account manager calls Change-Id: Ic88139f6ef34046eb3b551d8a75d5f97e1fe3d52 --- AndroidManifest.xml | 1 - src/com/android/launcher3/LauncherClings.java | 11 +---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 1fb8e8d01a..3af38f3847 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -53,7 +53,6 @@ - diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index c13752cb1f..c44ea6bcdc 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -44,8 +44,6 @@ class LauncherClings implements OnClickListener { private static final String TAG_CROP_TOP_AND_SIDES = "crop_bg_top_and_sides"; - private static final boolean DISABLE_CLINGS = false; - private static final int SHOW_CLING_DURATION = 250; private static final int DISMISS_CLING_DURATION = 200; @@ -215,10 +213,6 @@ class LauncherClings implements OnClickListener { /** Returns whether the clings are enabled or should be shown */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private boolean areClingsEnabled() { - if (DISABLE_CLINGS) { - return false; - } - // disable clings when running in a test harness if(ActivityManager.isRunningInTestHarness()) return false; @@ -231,10 +225,7 @@ class LauncherClings implements OnClickListener { // Restricted secondary users (child mode) will potentially have very few apps // seeded when they start up for the first time. Clings won't work well with that - boolean supportsLimitedUsers = - android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2; - Account[] accounts = AccountManager.get(mLauncher).getAccounts(); - if (supportsLimitedUsers && accounts.length == 0) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE); Bundle restrictions = um.getUserRestrictions(); if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) { From fb0f1095390d19d2cd6be127eb1aa3a83259b012 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 4 Aug 2015 16:02:52 -0700 Subject: [PATCH 28/76] Updating the default layouts for launcher3 Change-Id: I088c7195ae1985788e845900463d22973a0f61d4 --- res/xml/default_workspace_4x4.xml | 103 +++++------------------------ res/xml/default_workspace_5x5.xml | 100 +++++----------------------- res/xml/default_workspace_5x6.xml | 104 ++++-------------------------- res/xml/dw_phone_hotseat.xml | 63 ++++++++++++++++++ res/xml/dw_tablet_hotseat.xml | 78 ++++++++++++++++++++++ 5 files changed, 187 insertions(+), 261 deletions(-) create mode 100644 res/xml/dw_phone_hotseat.xml create mode 100644 res/xml/dw_tablet_hotseat.xml diff --git a/res/xml/default_workspace_4x4.xml b/res/xml/default_workspace_4x4.xml index 9bec86aa8f..060a1f8802 100644 --- a/res/xml/default_workspace_4x4.xml +++ b/res/xml/default_workspace_4x4.xml @@ -15,102 +15,33 @@ --> - - - + + - - - - - - - - - - - - + - - - - - + launcher:y="3" > + + - - - - - - - - - - + launcher:y="3" > + + - - - - + + + diff --git a/res/xml/default_workspace_5x5.xml b/res/xml/default_workspace_5x5.xml index 9bec86aa8f..3226617209 100644 --- a/res/xml/default_workspace_5x5.xml +++ b/res/xml/default_workspace_5x5.xml @@ -15,102 +15,34 @@ --> - - - + + - - - - - - - - - - - - + - - - + launcher:y="4" > + + - - + launcher:y="4" > + + - - - - - - - - + - - - - + launcher:y="4" > + + - diff --git a/res/xml/default_workspace_5x6.xml b/res/xml/default_workspace_5x6.xml index d42a93a6ac..bc236fb142 100644 --- a/res/xml/default_workspace_5x6.xml +++ b/res/xml/default_workspace_5x6.xml @@ -15,101 +15,23 @@ --> - - - + + + + - - - - - - - - - - - - - - - - - - - - - - + launcher:y="4" + launcher:uri="#Intent;action=android.intent.action.MAIN;category=android.intent.category.APP_CONTACTS;end" /> - - - - - - - - - - - - - + launcher:y="4" > + + + diff --git a/res/xml/dw_phone_hotseat.xml b/res/xml/dw_phone_hotseat.xml new file mode 100644 index 0000000000..b58994d1d7 --- /dev/null +++ b/res/xml/dw_phone_hotseat.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/res/xml/dw_tablet_hotseat.xml b/res/xml/dw_tablet_hotseat.xml new file mode 100644 index 0000000000..671ccba3c5 --- /dev/null +++ b/res/xml/dw_tablet_hotseat.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 006ee269ba2e9946a83b42f96d4a0296254cba4a Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Mon, 3 Aug 2015 14:40:11 -0700 Subject: [PATCH 29/76] Refactoring search bar animations. - This refactoring ensures that LauncherStateTransition does not do its own animation on the QSB, and that all animations to the SearchDropTargetBar go through its own animators. Bug: 22515084 Change-Id: Ia7d13c44d861eac7517076b52a9651a90911ed0a --- src/com/android/launcher3/DeviceProfile.java | 3 +- src/com/android/launcher3/DragLayer.java | 4 +- src/com/android/launcher3/Folder.java | 2 +- src/com/android/launcher3/Launcher.java | 48 ++-- .../LauncherStateTransitionAnimation.java | 214 ++++++++++------- .../LauncherViewPropertyAnimator.java | 4 +- .../launcher3/SearchDropTargetBar.java | 225 +++++++++--------- src/com/android/launcher3/Workspace.java | 25 +- .../WorkspaceStateTransitionAnimation.java | 77 +----- 9 files changed, 291 insertions(+), 311 deletions(-) diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index 6e90fc291b..774594fe2c 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -25,7 +25,6 @@ import android.graphics.Paint.FontMetrics; import android.graphics.Point; import android.graphics.Rect; import android.util.DisplayMetrics; -import android.util.Size; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -385,7 +384,7 @@ public class DeviceProfile { final boolean isLayoutRtl = Utilities.isRtl(launcher.getResources()); // Layout the search bar space - View searchBar = launcher.getSearchBar(); + View searchBar = launcher.getSearchDropTargetBar(); lp = (FrameLayout.LayoutParams) searchBar.getLayoutParams(); if (hasVerticalBarLayout) { // Vertical search bar space -- The search bar is fixed in the layout to be on the left diff --git a/src/com/android/launcher3/DragLayer.java b/src/com/android/launcher3/DragLayer.java index aaa14e6a6c..1c18747c10 100644 --- a/src/com/android/launcher3/DragLayer.java +++ b/src/com/android/launcher3/DragLayer.java @@ -159,7 +159,7 @@ public class DragLayer extends InsettableFrameLayout { } private boolean isEventOverDropTargetBar(MotionEvent ev) { - getDescendantRectRelativeToSelf(mLauncher.getSearchBar(), mHitRect); + getDescendantRectRelativeToSelf(mLauncher.getSearchDropTargetBar(), mHitRect); if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) { return true; } @@ -321,7 +321,7 @@ public class DragLayer extends InsettableFrameLayout { childrenForAccessibility.add(currentFolder); if (isInAccessibleDrag()) { - childrenForAccessibility.add(mLauncher.getSearchBar()); + childrenForAccessibility.add(mLauncher.getSearchDropTargetBar()); } } else { super.addChildrenForAccessibility(childrenForAccessibility); diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 2e19f6eba2..535d1a443d 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -275,7 +275,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList @Override public void enableAccessibleDrag(boolean enable) { - mLauncher.getSearchBar().enableAccessibleDrag(enable); + mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable); for (int i = 0; i < mContent.getChildCount(); i++) { mContent.getPageAt(i).enableAccessibleDrag(enable, CellLayout.FOLDER_ACCESSIBILITY_DRAG); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 15e43b2b90..aee044097b 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -132,8 +132,7 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class Launcher extends Activity implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks, - View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener, - LauncherStateTransitionAnimation.Callbacks { + View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener { static final String TAG = "Launcher"; static final boolean LOGD = false; @@ -452,7 +451,7 @@ public class Launcher extends Activity mDragController = new DragController(this); mInflater = getLayoutInflater(); - mStateTransitionAnimation = new LauncherStateTransitionAnimation(this, this); + mStateTransitionAnimation = new LauncherStateTransitionAnimation(this); mStats = new Stats(this); @@ -1844,7 +1843,7 @@ public class Launcher extends Activity return mOverviewPanel; } - public SearchDropTargetBar getSearchBar() { + public SearchDropTargetBar getSearchDropTargetBar() { return mSearchDropTargetBar; } @@ -3265,14 +3264,6 @@ public class Launcher extends Activity } } - @Override - public void onStateTransitionHideSearchBar() { - // Hide the search bar - if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.hideSearchBar(false /* animated */); - } - } - public void showWorkspace(boolean animated) { showWorkspace(WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null); } @@ -3290,16 +3281,9 @@ public class Launcher extends Activity boolean changed = mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL; if (changed) { - boolean wasInSpringLoadedMode = (mState != State.WORKSPACE); mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.NORMAL, - snapToPage, animated, onCompleteRunnable); - - // Show the search bar (only animate if we were showing the drop target bar in spring - // loaded mode) - if (mSearchDropTargetBar != null) { - mSearchDropTargetBar.showSearchBar(animated && wasInSpringLoadedMode); - } + mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), + Workspace.State.NORMAL, snapToPage, animated, onCompleteRunnable); // Set focus to the AppsCustomize button if (mAllAppsButton != null) { @@ -3323,7 +3307,8 @@ public class Launcher extends Activity void showOverviewMode(boolean animated) { mWorkspace.setVisibility(View.VISIBLE); - mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.OVERVIEW, + mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), + Workspace.State.OVERVIEW, WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, animated, null /* onCompleteRunnable */); mState = State.WORKSPACE; @@ -3378,9 +3363,10 @@ public class Launcher extends Activity } if (toState == State.APPS) { - mStateTransitionAnimation.startAnimationToAllApps(animated, focusSearchBar); + mStateTransitionAnimation.startAnimationToAllApps(mWorkspace.getState(), animated, + focusSearchBar); } else { - mStateTransitionAnimation.startAnimationToWidgets(animated); + mStateTransitionAnimation.startAnimationToWidgets(mWorkspace.getState(), animated); } // Change the state *after* we've called all the transition code @@ -3402,10 +3388,9 @@ public class Launcher extends Activity * new state. */ public Animator startWorkspaceStateChangeAnimation(Workspace.State toState, int toPage, - boolean animated, boolean hasOverlaySearchBar, HashMap layerViews) { + boolean animated, HashMap layerViews) { Workspace.State fromState = mWorkspace.getState(); - Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, - hasOverlaySearchBar, layerViews); + Animator anim = mWorkspace.setStateWithAnimation(toState, toPage, animated, layerViews); updateInteraction(fromState, toState); return anim; } @@ -3417,7 +3402,8 @@ public class Launcher extends Activity return; } - mStateTransitionAnimation.startAnimationToWorkspace(mState, Workspace.State.SPRING_LOADED, + mStateTransitionAnimation.startAnimationToWorkspace(mState, mWorkspace.getState(), + Workspace.State.SPRING_LOADED, WorkspaceStateTransitionAnimation.SCROLL_TO_CURRENT_PAGE, true /* animated */, null /* onCompleteRunnable */); mState = isAppsViewVisible() ? State.APPS_SPRING_LOADED : State.WIDGETS_SPRING_LOADED; @@ -4517,14 +4503,16 @@ public class Launcher extends Activity if (mWorkspace != null) mWorkspace.setAlpha(1f); if (mHotseat != null) mHotseat.setAlpha(1f); if (mPageIndicators != null) mPageIndicators.setAlpha(1f); - if (mSearchDropTargetBar != null) mSearchDropTargetBar.showSearchBar(false); + if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState( + SearchDropTargetBar.State.SEARCH_BAR, 0); } void hideWorkspaceSearchAndHotseat() { if (mWorkspace != null) mWorkspace.setAlpha(0f); if (mHotseat != null) mHotseat.setAlpha(0f); if (mPageIndicators != null) mPageIndicators.setAlpha(0f); - if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false); + if (mSearchDropTargetBar != null) mSearchDropTargetBar.animateToState( + SearchDropTargetBar.State.INVISIBLE, 0); } // TODO: These method should be a part of LauncherSearchCallback diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index d69b7432de..acace82990 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -79,13 +79,6 @@ import java.util.HashMap; */ public class LauncherStateTransitionAnimation { - /** - * Callbacks made during the state transition - */ - interface Callbacks { - public void onStateTransitionHideSearchBar(); - } - /** * Private callbacks made during transition setup. */ @@ -111,12 +104,10 @@ public class LauncherStateTransitionAnimation { public static final int SINGLE_FRAME_DELAY = 16; @Thunk Launcher mLauncher; - @Thunk Callbacks mCb; - @Thunk AnimatorSet mStateAnimation; + @Thunk AnimatorSet mCurrentAnimation; - public LauncherStateTransitionAnimation(Launcher l, Callbacks cb) { + public LauncherStateTransitionAnimation(Launcher l) { mLauncher = l; - mCb = cb; } /** @@ -125,8 +116,8 @@ public class LauncherStateTransitionAnimation { * @param startSearchAfterTransition Immediately starts app search after the transition to * All Apps is completed. */ - public void startAnimationToAllApps(final boolean animated, - final boolean startSearchAfterTransition) { + public void startAnimationToAllApps(final Workspace.State fromWorkspaceState, + final boolean animated, final boolean startSearchAfterTransition) { final AllAppsContainerView toView = mLauncher.getAppsView(); final View buttonView = mLauncher.getAllAppsButton(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @@ -159,15 +150,16 @@ public class LauncherStateTransitionAnimation { } }; // Only animate the search bar if animating from spring loaded mode back to all apps - startAnimationToOverlay(Workspace.State.NORMAL_HIDDEN, buttonView, toView, - toView.getContentView(), toView.getRevealView(), toView.getSearchBarView(), - animated, true /* hideSearchBar */, cb); + mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState, + Workspace.State.NORMAL_HIDDEN, buttonView, toView, toView.getContentView(), + toView.getRevealView(), toView.getSearchBarView(), animated, cb); } /** * Starts an animation to the widgets view. */ - public void startAnimationToWidgets(final boolean animated) { + public void startAnimationToWidgets(final Workspace.State fromWorkspaceState, + final boolean animated) { final WidgetsContainerView toView = mLauncher.getWidgetsView(); final View buttonView = mLauncher.getWidgetsButton(); @@ -177,17 +169,17 @@ public class LauncherStateTransitionAnimation { return 0.3f; } }; - startAnimationToOverlay(Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, - toView.getContentView(), toView.getRevealView(), null, animated, - true /* hideSearchBar */, cb); + mCurrentAnimation = startAnimationToOverlay(fromWorkspaceState, + Workspace.State.OVERVIEW_HIDDEN, buttonView, toView, toView.getContentView(), + toView.getRevealView(), null, animated, cb); } /** * Starts and animation to the workspace from the current overlay view. */ public void startAnimationToWorkspace(final Launcher.State fromState, - final Workspace.State toWorkspaceState, final int toWorkspacePage, - final boolean animated, final Runnable onCompleteRunnable) { + final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, + final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) { if (toWorkspaceState != Workspace.State.NORMAL && toWorkspaceState != Workspace.State.SPRING_LOADED && toWorkspaceState != Workspace.State.OVERVIEW) { @@ -195,10 +187,10 @@ public class LauncherStateTransitionAnimation { } if (fromState == Launcher.State.APPS || fromState == Launcher.State.APPS_SPRING_LOADED) { - startAnimationToWorkspaceFromAllApps(toWorkspaceState, toWorkspacePage, + startAnimationToWorkspaceFromAllApps(fromWorkspaceState, toWorkspaceState, toWorkspacePage, animated, onCompleteRunnable); } else { - startAnimationToWorkspaceFromWidgets(toWorkspaceState, toWorkspacePage, + startAnimationToWorkspaceFromWidgets(fromWorkspaceState, toWorkspaceState, toWorkspacePage, animated, onCompleteRunnable); } } @@ -207,10 +199,11 @@ public class LauncherStateTransitionAnimation { * Creates and starts a new animation to a particular overlay view. */ @SuppressLint("NewApi") - private void startAnimationToOverlay(final Workspace.State toWorkspaceState, - final View buttonView, final View toView, final View contentView, final View revealView, - final View overlaySearchBarView, final boolean animated, final boolean hideSearchBar, - final PrivateTransitionCallbacks pCb) { + private AnimatorSet startAnimationToOverlay(final Workspace.State fromWorkspaceState, + final Workspace.State toWorkspaceState, final View buttonView, final View toView, + final View contentView, final View revealView, final View overlaySearchBarView, + final boolean animated, final PrivateTransitionCallbacks pCb) { + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); @@ -230,11 +223,13 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, -1, - animated, overlaySearchBarView != null /* hasOverlaySearchBar */, layerViews); + animated, layerViews); + + // Animate the search bar + startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState, + animated ? revealDuration : 0, overlaySearchBarView); if (animated && initialized) { - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - // Setup the reveal view animation int width = revealView.getMeasuredWidth(); int height = revealView.getMeasuredHeight(); @@ -274,7 +269,7 @@ public class LauncherStateTransitionAnimation { // Play the animation layerViews.put(revealView, BUILD_AND_SET_LAYER); - mStateAnimation.play(panelAlphaAndDrift); + animation.play(panelAlphaAndDrift); if (overlaySearchBarView != null) { overlaySearchBarView.setAlpha(0f); @@ -282,7 +277,7 @@ public class LauncherStateTransitionAnimation { searchBarAlpha.setDuration(100); searchBarAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER); - mStateAnimation.play(searchBarAlpha); + animation.play(searchBarAlpha); } // Setup the animation for the content view @@ -297,13 +292,13 @@ public class LauncherStateTransitionAnimation { pageDrift.setDuration(revealDuration); pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0)); pageDrift.setStartDelay(itemsAlphaStagger); - mStateAnimation.play(pageDrift); + animation.play(pageDrift); ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 0f, 1f); itemsAlpha.setDuration(revealDuration); itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f)); itemsAlpha.setStartDelay(itemsAlphaStagger); - mStateAnimation.play(itemsAlpha); + animation.play(itemsAlpha); if (material) { float startRadius = pCb.getMaterialRevealViewStartFinalRadius(); @@ -316,10 +311,10 @@ public class LauncherStateTransitionAnimation { if (listener != null) { reveal.addListener(listener); } - mStateAnimation.play(reveal); + animation.play(reveal); } - mStateAnimation.addListener(new AnimatorListenerAdapter() { + animation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { dispatchOnLauncherTransitionEnd(fromView, animated, false); @@ -335,12 +330,8 @@ public class LauncherStateTransitionAnimation { } } - if (hideSearchBar) { - mCb.onStateTransitionHideSearchBar(); - } - // This can hold unnecessary references to views. - mStateAnimation = null; + cleanupAnimation(); pCb.onTransitionComplete(); } @@ -348,7 +339,7 @@ public class LauncherStateTransitionAnimation { // Play the workspace animation if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); + animation.play(workspaceAnim); } // Dispatch the prepare transition signal @@ -356,12 +347,12 @@ public class LauncherStateTransitionAnimation { dispatchOnLauncherTransitionPrepare(toView, animated, false); - final AnimatorSet stateAnimation = mStateAnimation; + final AnimatorSet stateAnimation = animation; final Runnable startAnimRunnable = new Runnable() { public void run() { - // Check that mStateAnimation hasn't changed while + // Check that mCurrentAnimation hasn't changed while // we waited for a layout/draw pass - if (mStateAnimation != stateAnimation) + if (mCurrentAnimation != stateAnimation) return; dispatchOnLauncherTransitionStart(fromView, animated, false); dispatchOnLauncherTransitionStart(toView, animated, false); @@ -380,12 +371,14 @@ public class LauncherStateTransitionAnimation { // Focus the new view toView.requestFocus(); - mStateAnimation.start(); + stateAnimation.start(); } }; toView.bringToFront(); toView.setVisibility(View.VISIBLE); toView.post(startAnimRunnable); + + return animation; } else { toView.setTranslationX(0.0f); toView.setTranslationY(0.0f); @@ -397,10 +390,6 @@ public class LauncherStateTransitionAnimation { // Show the content view contentView.setVisibility(View.VISIBLE); - if (hideSearchBar) { - mCb.onStateTransitionHideSearchBar(); - } - dispatchOnLauncherTransitionPrepare(fromView, animated, false); dispatchOnLauncherTransitionStart(fromView, animated, false); dispatchOnLauncherTransitionEnd(fromView, animated, false); @@ -408,18 +397,19 @@ public class LauncherStateTransitionAnimation { dispatchOnLauncherTransitionStart(toView, animated, false); dispatchOnLauncherTransitionEnd(toView, animated, false); pCb.onTransitionComplete(); + + return null; } } /** * Starts and animation to the workspace from the apps view. */ - private void startAnimationToWorkspaceFromAllApps(final Workspace.State toWorkspaceState, - final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) { + private void startAnimationToWorkspaceFromAllApps(final Workspace.State fromWorkspaceState, + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { AllAppsContainerView appsView = mLauncher.getAppsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { - int[] mAllAppsToPanelDelta; - @Override float getMaterialRevealViewFinalAlpha(View revealView) { // No alpha anim from all apps @@ -451,8 +441,8 @@ public class LauncherStateTransitionAnimation { } }; // Only animate the search bar if animating to spring loaded mode from all apps - startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, - mLauncher.getAllAppsButton(), appsView, appsView.getContentView(), + mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, toWorkspaceState, + toWorkspacePage, mLauncher.getAllAppsButton(), appsView, appsView.getContentView(), appsView.getRevealView(), appsView.getSearchBarView(), animated, onCompleteRunnable, cb); } @@ -460,8 +450,9 @@ public class LauncherStateTransitionAnimation { /** * Starts and animation to the workspace from the widgets view. */ - private void startAnimationToWorkspaceFromWidgets(final Workspace.State toWorkspaceState, - final int toWorkspacePage, final boolean animated, final Runnable onCompleteRunnable) { + private void startAnimationToWorkspaceFromWidgets(final Workspace.State fromWorkspaceState, + final Workspace.State toWorkspaceState, final int toWorkspacePage, + final boolean animated, final Runnable onCompleteRunnable) { final WidgetsContainerView widgetsView = mLauncher.getWidgetsView(); PrivateTransitionCallbacks cb = new PrivateTransitionCallbacks() { @Override @@ -479,19 +470,21 @@ public class LauncherStateTransitionAnimation { }; } }; - startAnimationToWorkspaceFromOverlay(toWorkspaceState, toWorkspacePage, - mLauncher.getWidgetsButton(), widgetsView, widgetsView.getContentView(), - widgetsView.getRevealView(), null, animated, onCompleteRunnable, cb); + mCurrentAnimation = startAnimationToWorkspaceFromOverlay(fromWorkspaceState, + toWorkspaceState, toWorkspacePage, mLauncher.getWidgetsButton(), widgetsView, + widgetsView.getContentView(), widgetsView.getRevealView(), null, animated, + onCompleteRunnable, cb); } /** * Creates and starts a new animation to the workspace. */ - private void startAnimationToWorkspaceFromOverlay(final Workspace.State toWorkspaceState, - final int toWorkspacePage, final View buttonView, final View fromView, - final View contentView, final View revealView, final View overlaySearchBarView, - final boolean animated, final Runnable onCompleteRunnable, - final PrivateTransitionCallbacks pCb) { + private AnimatorSet startAnimationToWorkspaceFromOverlay(final Workspace.State fromWorkspaceState, + final Workspace.State toWorkspaceState, final int toWorkspacePage, final View buttonView, + final View fromView, final View contentView, final View revealView, + final View overlaySearchBarView, final boolean animated, final Runnable onCompleteRunnable, + final PrivateTransitionCallbacks pCb) { + final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final Resources res = mLauncher.getResources(); final boolean material = Utilities.isLmpOrAbove(); final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); @@ -511,15 +504,16 @@ public class LauncherStateTransitionAnimation { // Create the workspace animation. // NOTE: this call apparently also sets the state for the workspace if !animated Animator workspaceAnim = mLauncher.startWorkspaceStateChangeAnimation(toWorkspaceState, - toWorkspacePage, animated, overlaySearchBarView != null /* hasOverlaySearchBar */, - layerViews); + toWorkspacePage, animated, layerViews); + + // Animate the search bar + startWorkspaceSearchBarAnimation(animation, fromWorkspaceState, toWorkspaceState, + animated ? revealDuration : 0, overlaySearchBarView); if (animated && initialized) { - mStateAnimation = LauncherAnimUtils.createAnimatorSet(); - // Play the workspace animation if (workspaceAnim != null) { - mStateAnimation.play(workspaceAnim); + animation.play(workspaceAnim); } // hideAppsCustomizeHelper is called in some cases when it is already hidden @@ -558,14 +552,14 @@ public class LauncherStateTransitionAnimation { panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY); panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); panelDriftY.setInterpolator(decelerateInterpolator); - mStateAnimation.play(panelDriftY); + animation.play(panelDriftY); ObjectAnimator panelDriftX = ObjectAnimator.ofFloat(revealView, "translationX", 0, revealViewToXDrift); panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY); panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); panelDriftX.setInterpolator(decelerateInterpolator); - mStateAnimation.play(panelDriftX); + animation.play(panelDriftX); // Setup animation for the reveal panel alpha final float revealViewToAlpha = !material ? 0f : @@ -576,7 +570,7 @@ public class LauncherStateTransitionAnimation { panelAlpha.setDuration(material ? revealDuration : 150); panelAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); panelAlpha.setInterpolator(decelerateInterpolator); - mStateAnimation.play(panelAlpha); + animation.play(panelAlpha); } // Setup the animation for the content view @@ -589,13 +583,13 @@ public class LauncherStateTransitionAnimation { pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY); pageDrift.setInterpolator(decelerateInterpolator); pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY); - mStateAnimation.play(pageDrift); + animation.play(pageDrift); contentView.setAlpha(1f); ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(contentView, "alpha", 1f, 0f); itemsAlpha.setDuration(100); itemsAlpha.setInterpolator(decelerateInterpolator); - mStateAnimation.play(itemsAlpha); + animation.play(itemsAlpha); if (overlaySearchBarView != null) { overlaySearchBarView.setAlpha(1f); @@ -604,7 +598,7 @@ public class LauncherStateTransitionAnimation { searchAlpha.setInterpolator(decelerateInterpolator); searchAlpha.setStartDelay(material ? 0 : itemsAlphaStagger + SINGLE_FRAME_DELAY); layerViews.put(overlaySearchBarView, BUILD_AND_SET_LAYER); - mStateAnimation.play(searchAlpha); + animation.play(searchAlpha); } if (material) { @@ -620,14 +614,14 @@ public class LauncherStateTransitionAnimation { if (listener != null) { reveal.addListener(listener); } - mStateAnimation.play(reveal); + animation.play(reveal); } dispatchOnLauncherTransitionPrepare(fromView, animated, true); dispatchOnLauncherTransitionPrepare(toView, animated, true); } - mStateAnimation.addListener(new AnimatorListenerAdapter() { + animation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { fromView.setVisibility(View.GONE); @@ -657,18 +651,19 @@ public class LauncherStateTransitionAnimation { } // This can hold unnecessary references to views. - mStateAnimation = null; + cleanupAnimation(); pCb.onTransitionComplete(); } }); - final AnimatorSet stateAnimation = mStateAnimation; + final AnimatorSet stateAnimation = animation; final Runnable startAnimRunnable = new Runnable() { public void run() { - // Check that mStateAnimation hasn't changed while + // Check that mCurrentAnimation hasn't changed while // we waited for a layout/draw pass - if (mStateAnimation != stateAnimation) + if (mCurrentAnimation != stateAnimation) return; + dispatchOnLauncherTransitionStart(fromView, animated, false); dispatchOnLauncherTransitionStart(toView, animated, false); @@ -682,10 +677,12 @@ public class LauncherStateTransitionAnimation { v.buildLayer(); } } - mStateAnimation.start(); + stateAnimation.start(); } }; fromView.post(startAnimRunnable); + + return animation; } else { fromView.setVisibility(View.GONE); dispatchOnLauncherTransitionPrepare(fromView, animated, true); @@ -700,9 +697,44 @@ public class LauncherStateTransitionAnimation { if (onCompleteRunnable != null) { onCompleteRunnable.run(); } + + return null; } } + /** + * Coordinates the workspace search bar animation along with the launcher state animation. + */ + private void startWorkspaceSearchBarAnimation(AnimatorSet animation, + final Workspace.State fromWorkspaceState, final Workspace.State toWorkspaceState, int duration, + View overlaySearchBar) { + final SearchDropTargetBar.State toSearchBarState = + toWorkspaceState.getSearchDropTargetBarState(); + + if (overlaySearchBar != null) { + if ((toWorkspaceState == Workspace.State.NORMAL) && + (fromWorkspaceState == Workspace.State.NORMAL_HIDDEN)) { + // If we are transitioning from the overlay to the workspace, then show the + // workspace search bar immediately and let the overlay search bar fade out on top + mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0); + } else if (fromWorkspaceState == Workspace.State.NORMAL) { + // If we are transitioning from the workspace to the overlay, then keep the + // workspace search bar visible until the overlay search bar fades in on top + animation.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, 0); + } + }); + } else { + // Otherwise, then just animate the workspace search bar normally + mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration); + } + } else { + // If there is no overlay search bar, then just animate the workspace search bar + mLauncher.getSearchDropTargetBar().animateToState(toSearchBarState, duration); + } + } /** * Dispatches the prepare-transition event to suitable views. @@ -753,10 +785,14 @@ public class LauncherStateTransitionAnimation { * Cancels the current animation. */ private void cancelAnimation() { - if (mStateAnimation != null) { - mStateAnimation.setDuration(0); - mStateAnimation.cancel(); - mStateAnimation = null; + if (mCurrentAnimation != null) { + mCurrentAnimation.setDuration(0); + mCurrentAnimation.cancel(); + mCurrentAnimation = null; } } + + private void cleanupAnimation() { + mCurrentAnimation = null; + } } diff --git a/src/com/android/launcher3/LauncherViewPropertyAnimator.java b/src/com/android/launcher3/LauncherViewPropertyAnimator.java index 4cafbbfa66..4406a2c5cb 100644 --- a/src/com/android/launcher3/LauncherViewPropertyAnimator.java +++ b/src/com/android/launcher3/LauncherViewPropertyAnimator.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.EnumSet; public class LauncherViewPropertyAnimator extends Animator implements AnimatorListener { + enum Properties { TRANSLATION_X, TRANSLATION_Y, @@ -51,13 +52,12 @@ public class LauncherViewPropertyAnimator extends Animator implements AnimatorLi long mStartDelay; long mDuration; TimeInterpolator mInterpolator; - ArrayList mListeners; + ArrayList mListeners = new ArrayList<>(); boolean mRunning = false; FirstFrameAnimatorHelper mFirstFrameHelper; public LauncherViewPropertyAnimator(View target) { mTarget = target; - mListeners = new ArrayList(); } @Override diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index 4cdf1cac9a..fe89c15a73 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -18,12 +18,11 @@ package com.android.launcher3; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; -import android.animation.ObjectAnimator; -import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; +import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; @@ -33,17 +32,41 @@ import android.widget.FrameLayout; */ public class SearchDropTargetBar extends FrameLayout implements DragController.DragListener { - private static final int TRANSITION_DURATION = 200; + /** The different states that the search bar space can be in. */ + public enum State { + INVISIBLE (0f, 0f), + SEARCH_BAR (1f, 0f), + DROP_TARGET (0f, 1f); - private ObjectAnimator mShowDropTargetBarAnim; - private ValueAnimator mHideSearchBarAnim; + private final float mSearchBarAlpha; + private final float mDropTargetBarAlpha; + + State(float sbAlpha, float dtbAlpha) { + mSearchBarAlpha = sbAlpha; + mDropTargetBarAlpha = dtbAlpha; + } + + float getSearchBarAlpha() { + return mSearchBarAlpha; + } + + float getDropTargetBarAlpha() { + return mDropTargetBarAlpha; + } + } + + private static int DEFAULT_DRAG_FADE_DURATION = 175; + + private LauncherViewPropertyAnimator mDropTargetBarAnimator; + private LauncherViewPropertyAnimator mQSBSearchBarAnimator; private static final AccelerateInterpolator sAccelerateInterpolator = new AccelerateInterpolator(); - private boolean mIsSearchBarHidden; - private View mQSBSearchBar; + private State mState = State.SEARCH_BAR; + private View mQSB; private View mDropTargetBar; private boolean mDeferOnDragEnd = false; + private boolean mAccessibilityEnabled = false; // Drop targets private ButtonDropTarget mInfoDropTarget; @@ -75,39 +98,6 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D mUninstallDropTarget.setLauncher(launcher); } - public void setQsbSearchBar(View qsb) { - mQSBSearchBar = qsb; - if (mQSBSearchBar != null) { - mHideSearchBarAnim = LauncherAnimUtils.ofFloat(mQSBSearchBar, "alpha", 1f, 0f); - setupAnimation(mHideSearchBarAnim, mQSBSearchBar); - } else { - // Create a no-op animation of the search bar is null - mHideSearchBarAnim = ValueAnimator.ofFloat(0, 0); - mHideSearchBarAnim.setDuration(TRANSITION_DURATION); - } - } - - private void prepareStartAnimation(View v) { - // Enable the hw layers before the animation starts (will be disabled in the onAnimationEnd - // callback below) - if (v != null) { - v.setLayerType(View.LAYER_TYPE_HARDWARE, null); - } - } - - private void setupAnimation(ValueAnimator anim, final View v) { - anim.setInterpolator(sAccelerateInterpolator); - anim.setDuration(TRANSITION_DURATION); - anim.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - if (v != null) { - v.setLayerType(View.LAYER_TYPE_NONE, null); - } - } - }); - } - @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -124,72 +114,84 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D // Create the various fade animations mDropTargetBar.setAlpha(0f); - mShowDropTargetBarAnim = LauncherAnimUtils.ofFloat(mDropTargetBar, "alpha", 0f, 1f); - setupAnimation(mShowDropTargetBarAnim, mDropTargetBar); + mDropTargetBarAnimator = new LauncherViewPropertyAnimator(mDropTargetBar); + mDropTargetBarAnimator.setInterpolator(sAccelerateInterpolator); + mDropTargetBarAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + // Ensure that the view is visible for the animation + mDropTargetBar.setVisibility(View.VISIBLE); + } + + @Override + public void onAnimationEnd(Animator animation) { + if (mDropTargetBar != null) { + AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled); + } + } + }); } - /** - * Finishes all the animations on the search and drop target bars. - */ - public void finishAnimations() { - prepareStartAnimation(mDropTargetBar); - mShowDropTargetBarAnim.reverse(); - prepareStartAnimation(mQSBSearchBar); - mHideSearchBarAnim.reverse(); - } + public void setQsbSearchBar(View qsb) { + mQSB = qsb; + if (mQSB != null) { + // Update the search ber animation + mQSBSearchBarAnimator = new LauncherViewPropertyAnimator(mQSB); + mQSBSearchBarAnimator.setInterpolator(sAccelerateInterpolator); + mQSBSearchBarAnimator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + // Ensure that the view is visible for the animation + if (mQSB != null) { + mQSB.setVisibility(View.VISIBLE); + } + } - /** - * Shows the search bar. - */ - public void showSearchBar(boolean animated) { - if (!mIsSearchBarHidden) return; - if (animated) { - prepareStartAnimation(mQSBSearchBar); - mHideSearchBarAnim.reverse(); + @Override + public void onAnimationEnd(Animator animation) { + if (mQSB != null) { + AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled); + } + } + }); } else { - mHideSearchBarAnim.cancel(); - if (mQSBSearchBar != null) { - mQSBSearchBar.setAlpha(1f); + mQSBSearchBarAnimator = null; + } + } + + /** + * Animates the current search bar state to a new state. If the {@param duration} is 0, then + * the state is applied immediately. + */ + public void animateToState(State newState, int duration) { + if (mState != newState) { + mState = newState; + + // Update the accessibility state + AccessibilityManager am = (AccessibilityManager) + getContext().getSystemService(Context.ACCESSIBILITY_SERVICE); + mAccessibilityEnabled = am.isEnabled(); + + animateViewAlpha(mQSBSearchBarAnimator, mQSB, newState.getSearchBarAlpha(), + duration); + animateViewAlpha(mDropTargetBarAnimator, mDropTargetBar, newState.getDropTargetBarAlpha(), + duration); + } + } + + /** + * Convenience method to animate the alpha of a view using hardware layers. + */ + private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha, + int duration) { + if (v != null && Float.compare(v.getAlpha(), alpha) != 0) { + if (duration > 0) { + animator.alpha(alpha).withLayer().setDuration(duration).start(); + } else { + v.setAlpha(alpha); + AlphaUpdateListener.updateVisibility(v, mAccessibilityEnabled); } } - mIsSearchBarHidden = false; - } - - /** - * Hides the search bar. We only use this for clings. - */ - public void hideSearchBar(boolean animated) { - if (mIsSearchBarHidden) return; - if (animated) { - prepareStartAnimation(mQSBSearchBar); - mHideSearchBarAnim.start(); - } else { - mHideSearchBarAnim.cancel(); - if (mQSBSearchBar != null) { - mQSBSearchBar.setAlpha(0f); - } - } - mIsSearchBarHidden = true; - } - - /** - * Shows the drop target bar. - */ - public void showDeleteTarget() { - // Animate out the QSB search bar, and animate in the drop target bar - prepareStartAnimation(mDropTargetBar); - mShowDropTargetBarAnim.start(); - hideSearchBar(true); - } - - /** - * Hides the drop target bar. - */ - public void hideDeleteTarget() { - // Restore the QSB search bar, and animate out the drop target bar - prepareStartAnimation(mDropTargetBar); - mShowDropTargetBarAnim.reverse(); - showSearchBar(true); } /* @@ -197,9 +199,13 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D */ @Override public void onDragStart(DragSource source, Object info, int dragAction) { - showDeleteTarget(); + animateToState(State.DROP_TARGET, DEFAULT_DRAG_FADE_DURATION); } + /** + * This is called to defer hiding the delete drop target until the drop animation has completed, + * instead of hiding immediately when the drag has ended. + */ public void deferOnDragEnd() { mDeferOnDragEnd = true; } @@ -207,22 +213,25 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D @Override public void onDragEnd() { if (!mDeferOnDragEnd) { - hideDeleteTarget(); + animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION); } else { mDeferOnDragEnd = false; } } + /** + * @return the bounds of the QSB search bar. + */ public Rect getSearchBarBounds() { - if (mQSBSearchBar != null) { + if (mQSB != null) { final int[] pos = new int[2]; - mQSBSearchBar.getLocationOnScreen(pos); + mQSB.getLocationOnScreen(pos); final Rect rect = new Rect(); rect.left = pos[0]; rect.top = pos[1]; - rect.right = pos[0] + mQSBSearchBar.getWidth(); - rect.bottom = pos[1] + mQSBSearchBar.getHeight(); + rect.right = pos[0] + mQSB.getWidth(); + rect.bottom = pos[1] + mQSB.getHeight(); return rect; } else { return null; @@ -230,8 +239,8 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D } public void enableAccessibleDrag(boolean enable) { - if (mQSBSearchBar != null) { - mQSBSearchBar.setVisibility(enable ? View.GONE : View.VISIBLE); + if (mQSB != null) { + mQSB.setVisibility(enable ? View.GONE : View.VISIBLE); } mInfoDropTarget.enableAccessibleDrag(enable); mDeleteDropTarget.enableAccessibleDrag(enable); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 086934773f..bbcabe1c24 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -178,7 +178,24 @@ public class Workspace extends PagedView // State variable that indicates whether the pages are small (ie when you're // in all apps or customize mode) - enum State { NORMAL, NORMAL_HIDDEN, SPRING_LOADED, OVERVIEW, OVERVIEW_HIDDEN}; + enum State { + NORMAL (SearchDropTargetBar.State.SEARCH_BAR), + NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE), + SPRING_LOADED (SearchDropTargetBar.State.DROP_TARGET), + OVERVIEW (SearchDropTargetBar.State.INVISIBLE), + OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE); + + private final SearchDropTargetBar.State mBarState; + + State(SearchDropTargetBar.State searchBarState) { + mBarState = searchBarState; + } + + public SearchDropTargetBar.State getSearchDropTargetBarState() { + return mBarState; + } + }; + private State mState = State.NORMAL; private boolean mIsSwitchingState = false; @@ -1567,7 +1584,7 @@ public class Workspace extends PagedView // Reset our click listener setOnClickListener(mLauncher); } - mLauncher.getSearchBar().enableAccessibleDrag(enable); + mLauncher.getSearchDropTargetBar().enableAccessibleDrag(enable); mLauncher.getHotseat().getLayout() .enableAccessibleDrag(enable, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); } @@ -1971,10 +1988,10 @@ public class Workspace extends PagedView * to that new state. */ public Animator setStateWithAnimation(State toState, int toPage, boolean animated, - boolean hasOverlaySearchBar, HashMap layerViews) { + HashMap layerViews) { // Create the animation to the new state Animator workspaceAnim = mStateTransitionAnimation.getAnimationToState(mState, - toState, toPage, animated, hasOverlaySearchBar, layerViews); + toState, toPage, animated, layerViews); // Update the current state mState = toState; diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java index e908ccd812..54f63bbd88 100644 --- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java +++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java @@ -218,16 +218,13 @@ public class WorkspaceStateTransitionAnimation { } public AnimatorSet getAnimationToState(Workspace.State fromState, Workspace.State toState, - int toPage, boolean animated, boolean hasOverlaySearchBar, - HashMap layerViews) { + int toPage, boolean animated, HashMap layerViews) { AccessibilityManager am = (AccessibilityManager) mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE); final boolean accessibilityEnabled = am.isEnabled(); TransitionStates states = new TransitionStates(fromState, toState); - int duration = getAnimationDuration(states); - animateWorkspace(states, toPage, animated, duration, layerViews, - accessibilityEnabled); - animateSearchBar(states, animated, duration, hasOverlaySearchBar, layerViews, + int workspaceDuration = getAnimationDuration(states); + animateWorkspace(states, toPage, animated, workspaceDuration, layerViews, accessibilityEnabled); animateBackgroundGradient(states, animated, BACKGROUND_FADE_OUT_DURATION); return mStateAnimator; @@ -473,76 +470,10 @@ public class WorkspaceStateTransitionAnimation { } } - /** - * Coordinates with the workspace animation to animate the search bar. - * - * TODO: This should really be coordinated with the SearchDropTargetBar, otherwise the - * bar has no idea that it is hidden, and this has no idea what state the bar is - * actually in. - */ - private void animateSearchBar(TransitionStates states, boolean animated, int duration, - boolean hasOverlaySearchBar, final HashMap layerViews, - final boolean accessibilityEnabled) { - - // The search bar is only visible in the workspace - final View searchBar = mLauncher.getOrCreateQsbBar(); - if (searchBar != null) { - final boolean searchBarWillBeShown = states.stateIsNormal; - final float finalSearchBarAlpha = searchBarWillBeShown ? 1f : 0f; - if (animated) { - if (hasOverlaySearchBar) { - // If there is an overlay search bar, then we will coordinate with it. - mStateAnimator.addListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationStart(Animator animation) { - // If we are transitioning to a visible search bar, show it immediately - // and let the overlay search bar has faded out - if (searchBarWillBeShown) { - searchBar.setAlpha(finalSearchBarAlpha); - AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); - } - } - - @Override - public void onAnimationEnd(Animator animation) { - // If we are transitioning to a hidden search bar, hide it only after - // the overlay search bar has faded in - if (!searchBarWillBeShown) { - searchBar.setAlpha(finalSearchBarAlpha); - AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); - } - } - }); - } else { - // Otherwise, we can just do the normal animation - LauncherViewPropertyAnimator searchBarAlpha = - new LauncherViewPropertyAnimator(searchBar).alpha(finalSearchBarAlpha); - searchBarAlpha.addListener(new AlphaUpdateListener(searchBar, - accessibilityEnabled)); - searchBar.setLayerType(View.LAYER_TYPE_HARDWARE, null); - if (layerViews != null) { - // If layerViews is not null, we add these views, and indicate that - // the caller can manage layer state. - layerViews.put(searchBar, LauncherStateTransitionAnimation.BUILD_AND_SET_LAYER); - } else { - // Otherwise let the animator handle layer management. - searchBarAlpha.withLayer(); - } - searchBarAlpha.setDuration(duration); - mStateAnimator.play(searchBarAlpha); - } - } else { - // Set the search bar state immediately - searchBar.setAlpha(finalSearchBarAlpha); - AlphaUpdateListener.updateVisibility(searchBar, accessibilityEnabled); - } - } - } - /** * Animates the background scrim. Add to the state animator to prevent jankiness. * - * @param finalAlpha the final alpha for the background scrim + * @param states the current and final workspace states * @param animated whether or not to set the background alpha immediately * @duration duration of the animation */ From d1ea63f24a751521e6c35cc06be0e548e0b04f23 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 5 Aug 2015 12:32:47 -0700 Subject: [PATCH 30/76] Removing some synthetic method creation Change-Id: Icb6b38f78aa56ad2c11e2ccc8005994ac4b86a91 --- .../launcher3/LauncherStateTransitionAnimation.java | 2 +- src/com/android/launcher3/SearchDropTargetBar.java | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index acace82990..5a3f3dc7ac 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -792,7 +792,7 @@ public class LauncherStateTransitionAnimation { } } - private void cleanupAnimation() { + @Thunk void cleanupAnimation() { mCurrentAnimation = null; } } diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index fe89c15a73..1efdfb62d3 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -26,6 +26,8 @@ import android.view.accessibility.AccessibilityManager; import android.view.animation.AccelerateInterpolator; import android.widget.FrameLayout; +import com.android.launcher3.util.Thunk; + /* * Ths bar will manage the transition between the QSB search bar and the delete drop * targets so that each of the individual IconDropTargets don't have to. @@ -63,10 +65,10 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D new AccelerateInterpolator(); private State mState = State.SEARCH_BAR; - private View mQSB; - private View mDropTargetBar; + @Thunk View mQSB; + @Thunk View mDropTargetBar; private boolean mDeferOnDragEnd = false; - private boolean mAccessibilityEnabled = false; + @Thunk boolean mAccessibilityEnabled = false; // Drop targets private ButtonDropTarget mInfoDropTarget; From a2a039b66f05a1164f954387b1c12a8fb4063e92 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 5 Aug 2015 13:22:21 -0700 Subject: [PATCH 31/76] Using a reverse hashmap instead of indexOfValue in userManager Bug: 22980139 Change-Id: I87b633d194ff4e1529dd679dc02da573ed374207 --- .../launcher3/WidgetPreviewLoader.java | 11 +---------- .../compat/UserManagerCompatV17.java | 19 ++++++++++++++----- .../launcher3/compat/UserManagerCompatVL.java | 14 ++++++++------ 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 1fb795addb..3d5058769c 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -212,7 +212,6 @@ public class WidgetPreviewLoader { public void removeObsoletePreviews(ArrayList list) { Utilities.assertWorkerThread(); - LongSparseArray userIdCache = new LongSparseArray<>(); LongSparseArray> validPackages = new LongSparseArray<>(); for (Object obj : list) { @@ -227,15 +226,7 @@ public class WidgetPreviewLoader { pkg = info.provider.getPackageName(); } - int userIdIndex = userIdCache.indexOfValue(user); - final long userId; - if (userIdIndex < 0) { - userId = mUserManager.getSerialNumberForUser(user); - userIdCache.put(userId, user); - } else { - userId = userIdCache.keyAt(userIdIndex); - } - + final long userId = mUserManager.getSerialNumberForUser(user); HashSet packages = validPackages.get(userId); if (packages == null) { packages = new HashSet<>(); diff --git a/src/com/android/launcher3/compat/UserManagerCompatV17.java b/src/com/android/launcher3/compat/UserManagerCompatV17.java index 1687569f34..75203b7f3e 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatV17.java +++ b/src/com/android/launcher3/compat/UserManagerCompatV17.java @@ -23,10 +23,16 @@ import android.os.UserManager; import com.android.launcher3.util.LongArrayMap; +import java.util.HashMap; + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public class UserManagerCompatV17 extends UserManagerCompatV16 { protected LongArrayMap mUsers; + // Create a separate reverse map as LongArrayMap.indexOfValue checks if objects are same + // and not {@link Object#equals} + protected HashMap mUserToSerialMap; + protected UserManager mUserManager; UserManagerCompatV17(Context context) { @@ -35,9 +41,9 @@ public class UserManagerCompatV17 extends UserManagerCompatV16 { public long getSerialNumberForUser(UserHandleCompat user) { synchronized (this) { - if (mUsers != null) { - int index = mUsers.indexOfValue(user); - return (index >= 0) ? mUsers.keyAt(index) : 0; + if (mUserToSerialMap != null) { + Long serial = mUserToSerialMap.get(user); + return serial == null ? 0 : serial; } } return mUserManager.getSerialNumberForUser(user.getUser()); @@ -55,9 +61,12 @@ public class UserManagerCompatV17 extends UserManagerCompatV16 { @Override public void enableAndResetCache() { synchronized (this) { - mUsers = new LongArrayMap(); + mUsers = new LongArrayMap<>(); + mUserToSerialMap = new HashMap<>(); UserHandleCompat myUser = UserHandleCompat.myUserHandle(); - mUsers.put(mUserManager.getSerialNumberForUser(myUser.getUser()), myUser); + long serial = mUserManager.getSerialNumberForUser(myUser.getUser()); + mUsers.put(serial, myUser); + mUserToSerialMap.put(myUser, serial); } } } diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java index 04cd0fdb41..4d404db2bc 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVL.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -30,6 +30,7 @@ import com.android.launcher3.util.LongArrayMap; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; @TargetApi(Build.VERSION_CODES.LOLLIPOP) @@ -48,12 +49,15 @@ public class UserManagerCompatVL extends UserManagerCompatV17 { @Override public void enableAndResetCache() { synchronized (this) { - mUsers = new LongArrayMap(); + mUsers = new LongArrayMap<>(); + mUserToSerialMap = new HashMap<>(); List users = mUserManager.getUserProfiles(); if (users != null) { for (UserHandle user : users) { - mUsers.put(mUserManager.getSerialNumberForUser(user), - UserHandleCompat.fromUser(user)); + long serial = mUserManager.getSerialNumberForUser(user); + UserHandleCompat userCompat = UserHandleCompat.fromUser(user); + mUsers.put(serial, userCompat); + mUserToSerialMap.put(userCompat, serial); } } } @@ -64,9 +68,7 @@ public class UserManagerCompatVL extends UserManagerCompatV17 { synchronized (this) { if (mUsers != null) { List users = new ArrayList<>(); - for (UserHandleCompat user : mUsers) { - users.add(user); - } + users.addAll(mUserToSerialMap.keySet()); return users; } } From a1f79d3e16362d16c06b18fd5a06d93be9bbf1fd Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 5 Aug 2015 14:00:45 -0700 Subject: [PATCH 32/76] Fixing invisible search bar - We used to rely on the state transition to inflate the search widget after the permission to bind widgets was granted, which is fragile. Now we try and inflate the search widget after user grants the permission if necessary. Bug: 22515084 Bug: 22980143 Change-Id: Id111d263bc8a864e0fa652cfd0e07e66ec690fe7 --- src/com/android/launcher3/Launcher.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9d04770af6..32e7ee6812 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -742,6 +742,7 @@ public class Launcher extends Activity }; if (requestCode == REQUEST_BIND_APPWIDGET) { + // This is called only if the user did not previously have permissions to bind widgets final int appWidgetId = data != null ? data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1; if (resultCode == RESULT_CANCELED) { @@ -751,6 +752,10 @@ public class Launcher extends Activity } else if (resultCode == RESULT_OK) { addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); + + // When the user has granted permission to bind widgets, we should check to see if + // we can inflate the default search bar widget. + getOrCreateQsbBar(); } return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { From bedf9232eb67a420f0372d3ca135ca13194e603b Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Fri, 10 Jul 2015 12:38:30 -0700 Subject: [PATCH 33/76] Adding market search. Change-Id: Id41615653cd4fa76213add4595418ad0cc6e7852 --- res/drawable-hdpi/ic_arrow_back_grey.png | Bin 190 -> 152 bytes res/drawable-hdpi/ic_search_grey.png | Bin 743 -> 419 bytes res/drawable-mdpi/ic_arrow_back_grey.png | Bin 151 -> 117 bytes res/drawable-mdpi/ic_search_grey.png | Bin 497 -> 263 bytes .../all_apps_search_market_bg.xml | 19 +++ res/drawable-xhdpi/ic_arrow_back_grey.png | Bin 234 -> 151 bytes res/drawable-xhdpi/ic_search_grey.png | Bin 972 -> 497 bytes res/drawable-xxhdpi/ic_arrow_back_grey.png | Bin 308 -> 190 bytes res/drawable-xxhdpi/ic_search_grey.png | Bin 1473 -> 743 bytes res/drawable-xxxhdpi/ic_arrow_back_grey.png | Bin 359 -> 234 bytes res/drawable-xxxhdpi/ic_search_grey.png | Bin 1996 -> 972 bytes res/drawable/all_apps_search_market_bg.xml | 20 +++ res/drawable/horizontal_line.xml | 21 +++ res/layout/all_apps_empty_search.xml | 15 +- res/layout/all_apps_search_bar.xml | 13 +- res/layout/all_apps_search_market.xml | 29 ++++ res/layout/all_apps_search_market_divider.xml | 27 ++++ res/values/colors.xml | 1 + res/values/strings.xml | 7 +- src/com/android/launcher3/Launcher.java | 15 +- .../android/launcher3/LauncherCallbacks.java | 1 + .../allapps/AllAppsContainerView.java | 31 ++--- .../launcher3/allapps/AllAppsGridAdapter.java | 130 +++++++++++++----- .../allapps/AllAppsRecyclerView.java | 2 + .../allapps/AlphabeticalAppsList.java | 61 ++++++-- .../allapps/DefaultAppSearchController.java | 18 +-- .../launcher3/testing/LauncherExtension.java | 5 + 27 files changed, 327 insertions(+), 88 deletions(-) create mode 100644 res/drawable-v21/all_apps_search_market_bg.xml create mode 100644 res/drawable/all_apps_search_market_bg.xml create mode 100644 res/drawable/horizontal_line.xml create mode 100644 res/layout/all_apps_search_market.xml create mode 100644 res/layout/all_apps_search_market_divider.xml diff --git a/res/drawable-hdpi/ic_arrow_back_grey.png b/res/drawable-hdpi/ic_arrow_back_grey.png index ccd3900ddf0e69d99a7e318847415e17dd6658e2..c7c00886fc0460c97b61834b365a5b085231d713 100755 GIT binary patch literal 152 zcmeAS@N?(olHy`uVBq!ia0vp^Dj>|k0wldT1B8K8yr+v}h{y5d1PRu|LQ~A9m<1f4 z@!#QwgKYA@>3KS-%lZs+t{9|UUSO2=xxgfiyR_$L*ey1%i$4?-lRH|rm~$M>ek)SJ ztGnUm%Bg|wy;8s241zopr00_P~ AVE_OC literal 190 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhawT0LDHLo)8Yy}X;Z!GOm#(0>AF zf=_wPCUpbu9Sb@%CJOpr+#C16W82w|jt-4auR^*=ajkr)F+pz5Wpn+gIgETV z76lIuG%_#G_IuP(7}v1x=@*7c(K8j*{jSL(y$KKnF2+y85}Sb4q9e0K8L08vpFo6u#6qxnZDP4#Zy{J&<_6+V6Z9U0GRNu~%fvV%rmHy5)ETZG8j zeDFPDcU;t2-a&|VFjPc{v2UJ7h)FP1M~Iehp71t27-}Gd>zl{CV-XBh5Tfgwrx0Qh z3}q2wM#LvCBg8Nmim^n9l27`B5LKIV?P)8LQEIh_B)d(GsBl;3iCg^VSZnJ%sJ;fGchJim@r|&_z&%5m?h6C1xge+ zAml=uITteb zIx>biRql*3%6Y}aX|JV< zrTmfj6L%+Aa@?QO-stx3nQ*~mxwDuaO(DDQOt_@77PY0>m95G0K-v@6rzNyzE##eND2N}L#QAMHv8b?g1^=fDv5#-AACgcr64?^gy3&9gieCChU6~G_~3^H zzo8*CBMQ#@;1>lyr6H6S1uuPc>R*B%)DUX3&azRD^tg4FwHS@CDvF+y@IbckL$E7G zC-hryHJ?0?pDpsO(F$Lz!daKpdyCpMdZE|xew=lsBbgGfjb`}Z_=;G1YAw;?n5;6B zMmNORb^OEyW7WR4h%s(0r?1ZUnxe;+C5K=5|cz}vaTJdD;Yx%TP}1q|7>)}^@E!HV?-az z6dzG3vLd@OM|Dv$hE@icW}RcMDRILw8%#0iJVJR{Hzn8V-5ffU_JgR@*=VJMP{~DO zbt>&OUTMEVsUuW!D0Ti1O5Yo=v==BLhf?Epj4_6e2c=->{8b8$&Vy2rbi#xQ6DEx3 Z{037>p9cHt>oouX002ovPDHLkV1nM|U8ev5 diff --git a/res/drawable-mdpi/ic_arrow_back_grey.png b/res/drawable-mdpi/ic_arrow_back_grey.png index 11996efe3f710c98d035a90ab0927641f843ccc7..5892c77d536b14f8d1774404538cf712c0f70e8b 100755 GIT binary patch literal 117 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*18&4O<5R22v2@=~DY+1Oa(d6lW z@A~Ok6PMYvm^@u*5-R7`^~rvT=5c1t>kQ>V7ApnK_H4Q>Ur#}7p Q31|+3r>mdKI;Vst0Q|}+rvLx| literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DI8PVHkch)?CvN0zaNs$Tr=76D z!=$5!NBBVS#b*=WxhBTvdv~%h%yn_A`WhZx_4Rz%`hOjqwpCwV#Q$xd`L&+ohQG;! ylaGbfJF}A10=;!qv^pou>GZXWN)l%%DPrcRnNmLGu{Y2n1_n=8KbLh*2~7YQWHkZ+ diff --git a/res/drawable-mdpi/ic_search_grey.png b/res/drawable-mdpi/ic_search_grey.png index e83891c11cd38aa5f2974fbaa495674414143778..c386dbb381a8bfdc2857b33c5badfecd1a1b617e 100755 GIT binary patch literal 263 zcmV+i0r>ujP)uqdu(AZ{v63@si|En<+Du&b|n_#O!t_#Kl3^F-^GF> zH7{xo%s&nH*`QW?`YB<9xU(cDqhLiq_|#*>ol%2~35Z!tI3kvYtr7c}P$P1~28e4+ zcp);wdWdK0hQhER;uaGQh?QX_ViyzUh`_i(COi<+_+mXGux3DyAtevQHl4ILNpBGs z`?Iz*W6zZ*H+D>&~tr8By7U(n|H6a_XYwRGkFp$UBYyy56Q1kgFlncdP&a N002ovPDHLkV1oXZZ9V`1 literal 497 zcmV(4}cZTXIavHlJWd({i?CfszwG!Jejt zY{>{Ew|oMXrnziMHzf~Zg1bKpj$&IBwk4yK-0=w%nwGaEiK|$;MfkDfls1X@~rhG-ips31G)D4P?97WBbD9cxr nLQs}ko)AJ%l3LygC*;L9+X5DqusM8900000NkvXXu0mjfMZ?}n diff --git a/res/drawable-v21/all_apps_search_market_bg.xml b/res/drawable-v21/all_apps_search_market_bg.xml new file mode 100644 index 0000000000..7bd2f8816f --- /dev/null +++ b/res/drawable-v21/all_apps_search_market_bg.xml @@ -0,0 +1,19 @@ + + + + + diff --git a/res/drawable-xhdpi/ic_arrow_back_grey.png b/res/drawable-xhdpi/ic_arrow_back_grey.png index 79b9b486c9bfee950fd4e9a9c4a8e78491cc137a..11996efe3f710c98d035a90ab0927641f843ccc7 100755 GIT binary patch literal 151 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}b0DI8PVHkch)?CvN0zaNs$Tr=76D z!=$5!NBBVS#b*=WxhBTvdv~%h%yn_A`WhZx_4Rz%`hOjqwpCwV#Q$xd`L&+ohQG;! ylaGbfJF}A10=;!qv^pou>GZXWN)l%%DPrcRnNmLGu{Y2n1_n=8KbLh*2~7YQWHkZ+ literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcgHhQ`^hE&{od-WvmAqN50i+l`4 zEeg+$8SP{&Y~0za(`UA9cDQ|`R^~JgpkjuI)@#w{*Z!Y#t@w4iuf!K)wKHEftJYko ze?KGo|FyC&FE20mpKn+D3(nwvS)X|Ft2pP4p!&v!+^?)67EY{sg^lT34!Em1Hnr4y z?C_qTu+Z;NToVhYsgRtIipPWx#+>aPXE>LiII8nd=BI>^(JOw==_gK4Dd2@TKwNu< UnT5E9m=lQW>FVdQ&MBb@00SypIsgCw diff --git a/res/drawable-xhdpi/ic_search_grey.png b/res/drawable-xhdpi/ic_search_grey.png index bd5fdf4446d4f0ceede6ae3ec23798e375333057..e83891c11cd38aa5f2974fbaa495674414143778 100755 GIT binary patch literal 497 zcmV(4}cZTXIavHlJWd({i?CfszwG!Jejt zY{>{Ew|oMXrnziMHzf~Zg1bKpj$&IBwk4yK-0=w%nwGaEiK|$;MfkDfls1X@~rhG-ips31G)D4P?97WBbD9cxr nLQs}ko)AJ%l3LygC*;L9+X5DqusM8900000NkvXXu0mjfMZ?}n literal 972 zcmV;-12g=IP)Q6U5| zpjV|r@F0+4LLvw$Sr5`6Gqbhb-I;c9-rTy~_j_-5K7R9?B}79*LqkJDLqo$N;713h zv4kX2ILA3sNMaGw=zy=-rB`DN-(X~Edx#=XjM7Wcg0-yD|8A>jE-v}C*vMIYrmdr< zIK-DChKs!A8!&Kz36vIEz7HQQiO;kxcnc~20e;w$Z@|D2LWPq52&cB?8!&K!hlP3i z=JLNFhA;vsg9|Q{A%HNZu${%ieffQ#-+9uQMV;wg^_WB2q$9~33%G?(Tp2G>o`dz* z+C^}$x)?^rH z;dGuVOH4JKd8|pi0XAi8x6Bn`xK>2VuVQB&TYL$IuTrdmF=pN`x5ejY)`d~A2I%d| ztZl}5ig8!$W|4m3b+$P}45zp?868xp&yKJrDb|QM1JhKfm?Nw+6zilo14~q>up_Lk z6l+490s3V-QeQxjV%-*JAVq~LbA;7Hu^x&ua88ACIl}6uSkF{9kXF459jiWwlj004 z3YkXRDAr|p3)z$-tZ|ApE-#gEV8%uXzRF7_d`#k1Z=17$;q;5lNB5{;bG8{5D8?PJ z`++F4p}A{|uY##?M8z5iFfaU-qv@f6d!l#6t4zozmbp3^t~cVV63v{_Y|vtX>=(n; zD5Em8&V=31|NIet92VgDUlyd;(&9mWCtj%K$WL1JP4uR1@OxfXiVu`r;%g?AF zf=_wPCUpbu9Sb@%CJOpr+#C16W82w|jt-4auR^*=ajkr)F+pz5Wpn+gIgETV z76lIuG%_#G_IuP(7}v1x=@*7c(K8j*{jSL(y$KKnF2+y85}Sb4q9e0K8L08vp>%KBk@HNZ zVx{@aYVHNIny)53`L5lp&TAlkY=VmCBnY~4XOo_G>yghj{DDs#Lp~{JRSHbC>F|G@{nd0Uj_Y$`rHIM~e2Pk?de@a`O_ALvu7?dn3(e`xSw&p zm3^;WNgfTB|ihv1@k;kgi&5>TkN+zluOVgV&Hq%X3<*c=z{|JSQzzF4Xu z@UYXx+^CboUuBoDr%Q#>#by_!L&`93sBHbtbZyUZH`mp%?|{L;;OXk;vd$@?2>|`B BbD#hK diff --git a/res/drawable-xxhdpi/ic_search_grey.png b/res/drawable-xxhdpi/ic_search_grey.png index 1d5c91361c1cd6ca4c4cd0f8e55653e94858bf44..f4c5e27d26bf76ca0a3675fe504a8c268a428d57 100755 GIT binary patch literal 743 zcmV?P)8LQEIh_B)d(GsBl;3iCg^VSZnJ%sJ;fGchJim@r|&_z&%5m?h6C1xge+ zAml=uITteb zIx>biRql*3%6Y}aX|JV< zrTmfj6L%+Aa@?QO-stx3nQ*~mxwDuaO(DDQOt_@77PY0>m95G0K-v@6rzNyzE##eND2N}L#QAMHv8b?g1^=fDv5#-AACgcr64?^gy3&9gieCChU6~G_~3^H zzo8*CBMQ#@;1>lyr6H6S1uuPc>R*B%)DUX3&azRD^tg4FwHS@CDvF+y@IbckL$E7G zC-hryHJ?0?pDpsO(F$Lz!daKpdyCpMdZE|xew=lsBbgGfjb`}Z_=;G1YAw;?n5;6B zMmNORb^OEyW7WR4h%s(0r?1ZUnxe;+C5K=5|cz}vaTJdD;Yx%TP}1q|7>)}^@E!HV?-az z6dzG3vLd@OM|Dv$hE@icW}RcMDRILw8%#0iJVJR{Hzn8V-5ffU_JgR@*=VJMP{~DO zbt>&OUTMEVsUuW!D0Ti1O5Yo=v==BLhf?Epj4_6e2c=->{8b8$&Vy2rbi#xQ6DEx3 Z{037>p9cHt>oouX002ovPDHLkV1nM|U8ev5 literal 1473 zcmV;y1wQ(TP)TOVFn)54Nfv%JriIp)8Z8t87Ht(*q9GE6f~81| zVyRj`aI5g7_1lezpkPc96SYvQXu(}qB1Ei8s}@Y7F^IUA1cl;PG|8N1(M2iq&gAya zJNL{@^7{{-_s*SjW-hVwM>Wf6;WVA}($4_HBVN%*FP)qQ8hHz`OYmhP%Q?n<0yIWI z53MvZaqI+I$Vr}}d3r)S3raoE1lDjHEx;|B$(3fHGFI~dO+YVAr59*EzoQAd&b-nI zRK;P2(1Z3brCx23x^+0QRiF)*$=IjP4;{ck1{n-qZ;|DY)9pthWD91T8 zMQ6xmDNtD+pPqm_9Az_$nMpMjBdVFnVz$sq7Xdm-?O6(RV3cV(S#)FPuaixa#?W39?UpgFuiWBkZ_NS3Q(t&76VP-0qV3JNmvW&)I-i*pf!)Xn7IOu>*Bz zuoLK*bNZ=8Je%#Dh>q9^bRP}XiKO5v8mik$py|#tT#uw+BN}R$X*L2ab6#N8NDAI@ z9?()7fm}oX9V8`pT?>wlK&R1AN0F4Yp`ki#1nNXXZAMbE6%BRSMxb6a)M6wh^{%bn zMxcH))J!BLwP>ivHUbTxp{kLT)S#hW*a$R?hN?hPQi+Bd%0!^?K(EYRVlQn3>Kp%( z9>2|d85Fk^eNen)d(~(y`l``xBM@k)E+i%Q&`{fK1ZqS>1x!!rafdoI*a%cjfQH(D zq+pZtfTr3AB=n%6t{^G+)%`rmN}yKf8O}mH`^fqEJ!~gXBkFJh@$3}pu*A}f!za!c zhcgh5KHv=+s?XAkL!ljY_zCgoJnGPfSPHZNMN?ln8c?TCYy}c-p$-q3jCe4WN2tSf z#9E+c)ag9p!9~>R3wwcb^rB863EP1>-J#50AfX9$3iujH&^Ks=&#nGW`UdJW$T}n; z8yG^JE+bY0&0`RC3TW|uaVZ0QY&Vc_2#s`sDFxoSE~1fkB9;SH(v3!XSilOw$zix=>GUGaEsuLW`3~NJ1qC z8AKEIn*Ed`|Ii4#O$Bn8%QZAXSExh&MT?V2NYYAr&;)d|0{IV(u`3IKgd9!WL|&w>7KpU!R=gn}rB-jdmzt$9)b^L{`I8Ot3U$r6Qo_hBElT?iym zxPNcjeBhYaI2_%p}0tqCL1rtahfdmprAb|ujfdmpr bpdafC?m9)~Vz%P)00000NkvXXu0mjf^4^xC diff --git a/res/drawable-xxxhdpi/ic_arrow_back_grey.png b/res/drawable-xxxhdpi/ic_arrow_back_grey.png index 854a9bd1a9a74af9350f0bb94fe2a680e1bf2a99..79b9b486c9bfee950fd4e9a9c4a8e78491cc137a 100755 GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD0wg^q?%xcgHhQ`^hE&{od-WvmAqN50i+l`4 zEeg+$8SP{&Y~0za(`UA9cDQ|`R^~JgpkjuI)@#w{*Z!Y#t@w4iuf!K)wKHEftJYko ze?KGo|FyC&FE20mpKn+D3(nwvS)X|Ft2pP4p!&v!+^?)67EY{sg^lT34!Em1Hnr4y z?C_qTu+Z;NToVhYsgRtIipPWx#+>aPXE>LiII8nd=BI>^(JOw==_gK4Dd2@TKwNu< UnT5E9m=lQW>FVdQ&MBb@00SypIsgCw literal 359 zcmeAS@N?(olHy`uVBq!ia0vp^2SAvG8AvYpRA>UER04cLT!C~cAs~34c^yz4cS(?6 zFhj#TBjfjVHMNrUF$^b^fx^!{T^vIy;@;lc&C6scz?Qh1`4g|A;$oIA1t8(IKqSgS zMeb34-v)PW};2su~N`bruRu zxiEQR1Jfj3o*i!!4n!RYXJX+HP;h8qV1zOnSbdpwI@ns^@B{mWa+!aVGMWBg$YA<^ z^CC;lgAdA|?{f;NcuY`mY--{4yWu^-z>rgj&*b4hQ6U5| zpjV|r@F0+4LLvw$Sr5`6Gqbhb-I;c9-rTy~_j_-5K7R9?B}79*LqkJDLqo$N;713h zv4kX2ILA3sNMaGw=zy=-rB`DN-(X~Edx#=XjM7Wcg0-yD|8A>jE-v}C*vMIYrmdr< zIK-DChKs!A8!&Kz36vIEz7HQQiO;kxcnc~20e;w$Z@|D2LWPq52&cB?8!&K!hlP3i z=JLNFhA;vsg9|Q{A%HNZu${%ieffQ#-+9uQMV;wg^_WB2q$9~33%G?(Tp2G>o`dz* z+C^}$x)?^rH z;dGuVOH4JKd8|pi0XAi8x6Bn`xK>2VuVQB&TYL$IuTrdmF=pN`x5ejY)`d~A2I%d| ztZl}5ig8!$W|4m3b+$P}45zp?868xp&yKJrDb|QM1JhKfm?Nw+6zilo14~q>up_Lk z6l+490s3V-QeQxjV%-*JAVq~LbA;7Hu^x&ua88ACIl}6uSkF{9kXF459jiWwlj004 z3YkXRDAr|p3)z$-tZ|ApE-#gEV8%uXzRF7_d`#k1Z=17$;q;5lNB5{;bG8{5D8?PJ z`++F4p}A{|uY##?M8z5iFfaU-qv@f6d!l#6t4zozmbp3^t~cVV63v{_Y|vtX>=(n; zD5Em8&V=31|NIet92VgDUlyd;(&9mWCtj%K$WL1JP4uR1@OxfXiVu`r;%g?G0000M*Nkl()$09r+1;6McJ=!QzSXm{Gv8rn19z~CLB@HPDb90=)oU(s zo+;j8j6ojYcG{8T5uy{2XAeiYggUy!2z%IA5Abze$2N}e8Cs%wUS=z)dVo^&FvL7s z26GJZ-Fg6b0e3Trme~Xaf_eZ;iu?E&t&l11B~`ltYz3y#3Omd8S`1+8VjQiKQF65u zz?NY@OK24>vX6Al1TfshBpQL!bW#t%Pxu!ak;@cnJ3xl#(1;C@uGIjI97ki|4H{}O zKpP*RF)=~5)&g|#H#A1hkgKHtUHk)$nNP{pN`N*>|Mssl!3#hid2+Opxu%sIdHR5Z zOtOxa=o7NF5TLQd-~PXIfbBFPhGzQsU5QuN1Pw_Kkl`3wq$Lh>Cm~XVkh^$^t7wtl zB%SO4!)Q@H=SkX-61DR*SJ0vyNOFK5qeWQdDO!-Psg(iN&?4+eYJi)HT68$UEl8Pc zgYYv$qTR_b#%5uJGebeqmKHM7NCnI=jV_{q(YjU zpTQRCNLGMx)X5(zeDP;^6?HO_qyRfmC%-2^DlFuA)XCPQ1V}OM`0WpnDhM5~f-{84 z2yma{s(c!$l8oa{#7~nDAmC%vbe2Y>ikdlxnm!`%Kfv9nX*q(UgT1Vvrau1zOroX- zkqGQYO{aYi(1V&zAdv_-iJH3o4lsloR=EXzGFkPazQ+L=Cf~{0*?p zUhk&`iO`LFff_dX8{i0PcoK=&0BShwYk+{y%+1C&Bx3F6#^s!^0rGx)x9sbboBa&1 z2Q}P@#K0!h@Q|MYj+zHeLL>%K%o-zo-Om6E-kd*p))?sp9|LSa4L?F+qJSFO{0nf0 zdC0H{iHRHd(irV+{sq`&9s`sZdEXfA{k{dTC;2ZRF>=Tl?K8dwu(#nsVq~xR$5!tG zyz9Qz;lXYAZT|vH86PQ+#7MU>+7JB;aNhVxIV489jM2_`7r=bIQY#W8S$itxTYy#L zBV~{nx!xG<3KFGyfORBF^`D?6Bue$0@B$L0`eW!h{{p;|sEOF5e*wn4T8ijT-cp3M z7nVs*UtdLy!lRu zb|hjOST^qfp|1g6Mh#CR5gSAehY()_Y())MB3_oxvWObC`5Pd`KV$wMtRd8Jk+hEi z+%NDoz8&Er^g3!7MtlzNJ=F9T5{ZCQs44Gv0AT_(J&Z))5!7@N@jXBRH7(PFRN41g zMNK>W4-hbgnx<*0@`3aj)bv*Z{{sj=N3k6vQ^i9lCs1tnB;|!oXHjf_Bt)tpLJp!%evqV>_Kl)WUL{lEAD`DyCr6Pa1<0|8Iyp{r1#i74P$yUT zmUjUR`%p*I^j7G>a|U&^8%bJ#G^bHVE9@pf$|&FwR!~RB2)qnn=;Si$>?HXzuKAoo zon7RcNZeH90H9%wL2fL=g7XmTsIz~w1&N=EE&m$6V1Ra{MBiYLMYJeSAW068<_)w+ zS2@BaQbkVwHuDP0Xp#Oz;C%o?0~2ToW_gwZHz0-|AU1Yibnk-#(Q=p$0`M^A6Xj!1g{{WU8pQ4_4+|vMtPR^j7_>HFl3|S`X38D-j zH1H-G1IPIsHSG5^fFaERG-AIceVj(i>=Zi*k^f7J((iu&OE&||qGfQA=lBj{cp}~j zgeaug%wgux63ugn?W7SWkE2D|o1g%On|X-Wxqv#FXN) z`-=k%^cxba^8g3{l=}$@Zu0;L0F?U<39bOzeT4)U0OJGz+I@xuX8_{_0NQ%o1$d0J@|EZ2+A^f(5`V z!DC(806K*P8vx@3bpQkaraMkwkPgxO00s#1`9lEh`||_H3HtK~0NVBE&j7UT&krCb z=+B=3Xz9-nASHNo=T8B&dG_`i00IC&Z2$oPAOHXa0Du4h5C8xI06+i$2mk;903ZMW e1OR{l0MI{a5w(t + + + + + + diff --git a/res/drawable/horizontal_line.xml b/res/drawable/horizontal_line.xml new file mode 100644 index 0000000000..3f3f17e351 --- /dev/null +++ b/res/drawable/horizontal_line.xml @@ -0,0 +1,21 @@ + + + + + + diff --git a/res/layout/all_apps_empty_search.xml b/res/layout/all_apps_empty_search.xml index f60c4a09ad..b9b493eab3 100644 --- a/res/layout/all_apps_empty_search.xml +++ b/res/layout/all_apps_empty_search.xml @@ -18,11 +18,14 @@ android:id="@+id/empty_text" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:gravity="center" - android:paddingTop="24dp" - android:paddingBottom="24dp" - android:paddingRight="@dimen/all_apps_grid_view_start_margin" - android:textSize="16sp" - android:textColor="#4c4c4c" + android:gravity="start" + android:paddingTop="20dp" + android:paddingBottom="8dp" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:fontFamily="sans-serif-medium" + android:textSize="14sp" + android:textColor="#212121" + android:alpha="0.56" android:focusable="false" /> diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml index cf30eac364..4947203df5 100644 --- a/res/layout/all_apps_search_bar.xml +++ b/res/layout/all_apps_search_bar.xml @@ -32,11 +32,10 @@ android:id="@+id/dismiss_search_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginLeft="4dp" - android:layout_marginStart="4dp" + android:layout_gravity="center_vertical" + android:layout_marginLeft="16dp" + android:layout_marginStart="16dp" android:contentDescription="@string/all_apps_button_label" - android:paddingBottom="13dp" - android:paddingTop="13dp" android:src="@drawable/ic_arrow_back_grey" /> \ No newline at end of file diff --git a/res/layout/all_apps_search_market.xml b/res/layout/all_apps_search_market.xml new file mode 100644 index 0000000000..1282069c88 --- /dev/null +++ b/res/layout/all_apps_search_market.xml @@ -0,0 +1,29 @@ + + + diff --git a/res/layout/all_apps_search_market_divider.xml b/res/layout/all_apps_search_market_divider.xml new file mode 100644 index 0000000000..39097818f7 --- /dev/null +++ b/res/layout/all_apps_search_market_divider.xml @@ -0,0 +1,27 @@ + + + \ No newline at end of file diff --git a/res/values/colors.xml b/res/values/colors.xml index 51e4d40a5b..0add48cd85 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -44,6 +44,7 @@ #009688 + #DDDDDD #42FFFFFF diff --git a/res/values/strings.xml b/res/values/strings.xml index 88f149bd0e..fefadef282 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -24,10 +24,10 @@ content://com.android.launcher2.settings/favorites?notify=true - + com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS - + com.android.launcher3.permission.RECEIVE_FIRST_LOAD_BROADCAST @@ -61,6 +61,9 @@ Loading Apps… No Apps found matching \"%1$s\" + + Go to %1$s diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9d04770af6..2d338e3d42 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2132,6 +2132,15 @@ public class Launcher extends Activity } } + public void startSearchFromAllApps(View v, Intent searchIntent, String searchQuery) { + if (mLauncherCallbacks != null && mLauncherCallbacks.startSearchFromAllApps(searchQuery)) { + return; + } + + // If not handled, then just start the provided search intent + startActivitySafely(v, searchIntent, null); + } + public boolean isOnCustomContent() { return mWorkspace.isOnOrMovingToCustomContent(); } @@ -2533,6 +2542,10 @@ public class Launcher extends Activity if (!isAppsViewVisible()) { showAppsView(true /* animated */, false /* resetListToTop */, true /* updatePredictedApps */, false /* focusSearchBar */); + + if (mLauncherCallbacks != null) { + mLauncherCallbacks.onClickAllAppsButton(v); + } } } @@ -2924,7 +2937,7 @@ public class Launcher extends Activity return false; } - @Thunk boolean startActivitySafely(View v, Intent intent, Object tag) { + public boolean startActivitySafely(View v, Intent intent, Object tag) { boolean success = false; if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) { Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show(); diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java index 6618cca781..e34bd57fdd 100644 --- a/src/com/android/launcher3/LauncherCallbacks.java +++ b/src/com/android/launcher3/LauncherCallbacks.java @@ -77,6 +77,7 @@ public interface LauncherCallbacks { public boolean providesSearch(); public boolean startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds); + public boolean startSearchFromAllApps(String query); @Deprecated public void startVoice(); public boolean hasCustomContentToLeft(); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index 010b2cb486..e129dc6d37 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -16,34 +16,26 @@ package com.android.launcher3.allapps; import android.annotation.SuppressLint; -import android.annotation.TargetApi; import android.content.Context; +import android.content.Intent; import android.content.res.Resources; import android.graphics.Point; import android.graphics.Rect; import android.graphics.drawable.InsetDrawable; -import android.os.Build; -import android.os.Bundle; import android.support.v7.widget.RecyclerView; import android.text.Selection; import android.text.SpannableStringBuilder; import android.text.method.TextKeyListener; import android.util.AttributeSet; import android.view.KeyEvent; -import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.widget.FrameLayout; import android.widget.LinearLayout; - import com.android.launcher3.AppInfo; import com.android.launcher3.BaseContainerView; -import com.android.launcher3.BubbleTextView; import com.android.launcher3.CellLayout; -import com.android.launcher3.CheckLongPressHelper; import com.android.launcher3.DeleteDropTarget; import com.android.launcher3.DeviceProfile; import com.android.launcher3.DragSource; @@ -53,7 +45,6 @@ import com.android.launcher3.ItemInfo; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherTransitionable; import com.android.launcher3.R; -import com.android.launcher3.Stats; import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; import com.android.launcher3.util.ComponentKey; @@ -155,6 +146,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc @Thunk AllAppsSearchBarController mSearchBarController; private ViewGroup mSearchBarContainerView; private View mSearchBarView; + private SpannableStringBuilder mSearchQueryBuilder = null; private int mSectionNamesMargin; private int mNumAppsPerRow; @@ -165,7 +157,13 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc // This coordinate is relative to its parent private final Point mIconLastTouchPos = new Point(); - private SpannableStringBuilder mSearchQueryBuilder = null; + private View.OnClickListener mSearchClickListener = new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent searchIntent = (Intent) v.getTag(); + mLauncher.startActivitySafely(v, searchIntent, null); + } + }; public AllAppsContainerView(Context context) { this(context, null); @@ -182,8 +180,7 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc mLauncher = (Launcher) context; mSectionNamesMargin = res.getDimensionPixelSize(R.dimen.all_apps_grid_view_start_margin); mApps = new AlphabeticalAppsList(context); - mAdapter = new AllAppsGridAdapter(context, mApps, this, mLauncher, this); - mAdapter.setEmptySearchText(res.getString(R.string.all_apps_loading_message)); + mAdapter = new AllAppsGridAdapter(mLauncher, mApps, this, mLauncher, this); mApps.setAdapter(mAdapter); mLayoutManager = mAdapter.getLayoutManager(); mItemDecoration = mAdapter.getItemDecoration(); @@ -615,13 +612,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc @Override public void onSearchResult(String query, ArrayList apps) { if (apps != null) { - if (apps.isEmpty()) { - String formatStr = getResources().getString(R.string.all_apps_no_search_results); - mAdapter.setEmptySearchText(String.format(formatStr, query)); - } else { - mAppsRecyclerView.scrollToTop(); - } mApps.setOrderedFilter(apps); + mAdapter.setLastSearchQuery(query); + mAppsRecyclerView.scrollToTop(); } } diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index e96567c413..4acfc5ca6a 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -16,14 +16,17 @@ package com.android.launcher3.allapps; import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PointF; import android.graphics.Rect; -import android.os.Handler; import android.support.v4.view.accessibility.AccessibilityRecordCompat; import android.support.v4.view.accessibility.AccessibilityEventCompat; +import android.net.Uri; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -34,6 +37,7 @@ import android.view.accessibility.AccessibilityEvent; import android.widget.TextView; import com.android.launcher3.AppInfo; import com.android.launcher3.BubbleTextView; +import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.Utilities; import com.android.launcher3.util.Thunk; @@ -58,6 +62,10 @@ class AllAppsGridAdapter extends RecyclerView.Adapter 1) { return false; } - // If there is exactly one icon, then quick-launch it + // Otherwise, find the first icon, or fallback to the search-market-view and launch it List items = mApps.getAdapterItems(); for (int i = 0; i < items.size(); i++) { AlphabeticalAppsList.AdapterItem item = items.get(i); - if (item.viewType == AllAppsGridAdapter.ICON_VIEW_TYPE) { - mAppsRecyclerView.getChildAt(i).performClick(); - mInputMethodManager.hideSoftInputFromWindow( - mContainerView.getWindowToken(), 0); - return true; + switch (item.viewType) { + case AllAppsGridAdapter.ICON_VIEW_TYPE: + case AllAppsGridAdapter.SEARCH_MARKET_VIEW_TYPE: + mAppsRecyclerView.getChildAt(i).performClick(); + mInputMethodManager.hideSoftInputFromWindow( + mContainerView.getWindowToken(), 0); + return true; } } return false; diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java index 34492e4cae..8702877bf5 100644 --- a/src/com/android/launcher3/testing/LauncherExtension.java +++ b/src/com/android/launcher3/testing/LauncherExtension.java @@ -201,6 +201,11 @@ public class LauncherExtension extends Launcher { return false; } + @Override + public boolean startSearchFromAllApps(String query) { + return false; + } + @Override public void startVoice() { } From cf0c746ec080ee528d4994c382562fda1e647752 Mon Sep 17 00:00:00 2001 From: Adam Cohen Date: Thu, 6 Aug 2015 14:02:23 -0700 Subject: [PATCH 34/76] Always log if Package is missing while loading default workspace Change-Id: I7f2e5a9aabdf1fc192ad7912aaefa3f41c059ff1 --- src/com/android/launcher3/AutoInstallsLayout.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index 99a98ddac2..440e4e7b99 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -355,7 +355,7 @@ public class AutoInstallsLayout { return addShortcut(info.loadLabel(mPackageManager).toString(), intent, Favorites.ITEM_TYPE_APPLICATION); } catch (PackageManager.NameNotFoundException e) { - if (LOGD) Log.w(TAG, "Unable to add favorite: " + packageName + "/" + className, e); + Log.e(TAG, "Unable to add favorite: " + packageName + "/" + className, e); } return -1; } else { @@ -367,7 +367,7 @@ public class AutoInstallsLayout { * Helper method to allow extending the parser capabilities */ protected long invalidPackageOrClass(XmlResourceParser parser) { - if (LOGD) Log.d(TAG, "Skipping invalid with no component"); + Log.w(TAG, "Skipping invalid with no component"); return -1; } } From b0ca1a225f99a0a9b70419ed11e313b78ea1425c Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 6 Aug 2015 14:42:50 -0700 Subject: [PATCH 35/76] Fixing issue with miscalculation in updating container bounds. - There was an issue with the previous logic where the test of whether the search bounds changed would always be false if valid search bounds were given. This in conjunction with the fact that the padding was changed to only take the bounds left/right into account, meant that the container bounds would not be updated if the search bar bounds shifted via the insets. Bug: 22918919 Change-Id: Ia810ddc7a56eae4afc0c7cd558fa6dc9e8e7a95b --- .../android/launcher3/BaseContainerView.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/com/android/launcher3/BaseContainerView.java b/src/com/android/launcher3/BaseContainerView.java index c8de9df10d..c11824054c 100644 --- a/src/com/android/launcher3/BaseContainerView.java +++ b/src/com/android/launcher3/BaseContainerView.java @@ -34,10 +34,12 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab // The bounds of the search bar. Only the left, top, right are used to inset the // search bar and the height is determined by the measurement of the layout private Rect mFixedSearchBarBounds = new Rect(); - // The bounds of the container + // The computed bounds of the search bar + private Rect mSearchBarBounds = new Rect(); + // The computed bounds of the container protected Rect mContentBounds = new Rect(); - // The padding to apply to the container to achieve the bounds - protected Rect mContentPadding = new Rect(); + // The computed padding to apply to the container to achieve the container bounds + private Rect mContentPadding = new Rect(); // The inset to apply to the edges and between the search bar and the container private int mContainerBoundsInset; private boolean mHasSearchBar; @@ -90,7 +92,7 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab */ protected void updateBackgroundAndPaddings() { Rect padding; - Rect searchBarBounds = new Rect(mFixedSearchBarBounds); + Rect searchBarBounds = new Rect(); if (!isValidSearchBarBounds(mFixedSearchBarBounds)) { // Use the default bounds padding = new Rect(mInsets.left + mContainerBoundsInset, @@ -110,14 +112,20 @@ public abstract class BaseContainerView extends LinearLayout implements Insettab (mHasSearchBar ? 0 : (mInsets.top + mContainerBoundsInset)), getMeasuredWidth() - mFixedSearchBarBounds.right, mInsets.bottom + mContainerBoundsInset); + + // Use the search bounds + searchBarBounds.set(mFixedSearchBarBounds); } - if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mFixedSearchBarBounds)) { + + // If either the computed container padding has changed, or the computed search bar bounds + // has changed, then notify the container + if (!padding.equals(mContentPadding) || !searchBarBounds.equals(mSearchBarBounds)) { mContentPadding.set(padding); mContentBounds.set(padding.left, padding.top, getMeasuredWidth() - padding.right, getMeasuredHeight() - padding.bottom); - mFixedSearchBarBounds.set(searchBarBounds); - onUpdateBackgroundAndPaddings(mFixedSearchBarBounds, padding); + mSearchBarBounds.set(searchBarBounds); + onUpdateBackgroundAndPaddings(mSearchBarBounds, padding); } } From b33e09a93cda9793b84ec70e1d85c9038f5d3ee6 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 10 Aug 2015 11:53:23 -0700 Subject: [PATCH 36/76] Preventing user from selecting any new time, when a tile is already being applied Bug: 22293299 Change-Id: I6b9bf2b777d33ca23f291c7872ce1f338d41e516 --- .../src/com/android/launcher3/WallpaperPickerActivity.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index c723b39be0..069638f8cd 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -551,6 +551,10 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { @Override public void onClick(View v) { if (mSelectedTile != null) { + // Prevent user from selecting any new tile. + mWallpaperStrip.setVisibility(View.GONE); + actionBar.hide(); + WallpaperTileInfo info = (WallpaperTileInfo) mSelectedTile.getTag(); info.onSave(WallpaperPickerActivity.this); } else { From 6579e1eee8a6fce44f020d40c3bbdbf245d6c12c Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 11 Aug 2015 12:10:34 -0700 Subject: [PATCH 37/76] Disabling home screen migration logic Change-Id: I506948852945bfb8ebd7a95fc951880ffe9a3e17 --- src/com/android/launcher3/LauncherBackupAgentHelper.java | 2 +- src/com/android/launcher3/LauncherBackupHelper.java | 4 +++- src/com/android/launcher3/LauncherModel.java | 3 +-- src/com/android/launcher3/model/MigrateFromRestoreTask.java | 2 ++ 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index 611ab2e928..3debef60e0 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -98,7 +98,7 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { LauncherAppState.getLauncherProvider().updateFolderItemsRank(); } - if (mHelper.shouldAttemptWorkspaceMigration()) { + if (MigrateFromRestoreTask.ENABLED && mHelper.shouldAttemptWorkspaceMigration()) { MigrateFromRestoreTask.markForMigration(getApplicationContext(), (int) mHelper.migrationCompatibleProfileData.desktopCols, (int) mHelper.migrationCompatibleProfileData.desktopRows, diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java index 136556b927..2d11d3af12 100644 --- a/src/com/android/launcher3/LauncherBackupHelper.java +++ b/src/com/android/launcher3/LauncherBackupHelper.java @@ -52,6 +52,7 @@ import com.android.launcher3.backup.BackupProtos.Screen; import com.android.launcher3.backup.BackupProtos.Widget; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; +import com.android.launcher3.model.MigrateFromRestoreTask; import com.android.launcher3.util.Thunk; import com.google.protobuf.nano.InvalidProtocolBufferNanoException; import com.google.protobuf.nano.MessageNano; @@ -314,7 +315,8 @@ public class LauncherBackupHelper implements BackupHelper { return true; } - if ((oldProfile.desktopCols - currentProfile.desktopCols <= 1) && + if (MigrateFromRestoreTask.ENABLED && + (oldProfile.desktopCols - currentProfile.desktopCols <= 1) && (oldProfile.desktopRows - currentProfile.desktopRows <= 1)) { // Allow desktop migration when row and/or column count contracts by 1. diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f029561a32..d9b0cd490d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1758,8 +1758,7 @@ public class LauncherModel extends BroadcastReceiver int countX = (int) profile.numColumns; int countY = (int) profile.numRows; - - if (MigrateFromRestoreTask.shouldRunTask(mContext)) { + if (MigrateFromRestoreTask.ENABLED && MigrateFromRestoreTask.shouldRunTask(mContext)) { long migrationStartTime = System.currentTimeMillis(); Log.v(TAG, "Starting workspace migration after restore"); try { diff --git a/src/com/android/launcher3/model/MigrateFromRestoreTask.java b/src/com/android/launcher3/model/MigrateFromRestoreTask.java index 8d4472fd18..6a529f61f7 100644 --- a/src/com/android/launcher3/model/MigrateFromRestoreTask.java +++ b/src/com/android/launcher3/model/MigrateFromRestoreTask.java @@ -37,6 +37,8 @@ import java.util.HashSet; */ public class MigrateFromRestoreTask { + public static boolean ENABLED = false; + private static final String TAG = "MigrateFromRestoreTask"; private static final boolean DEBUG = true; From 3fcab663a141b31916152c8bc185b93b718d79df Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 11 Aug 2015 15:17:30 -0700 Subject: [PATCH 38/76] Preventing zoom-in animation from running everytime the app updates. Bug: 22557455 Change-Id: I3ac1212a2f319397cd18be8f4452047d974312b8 --- src/com/android/launcher3/PreloadIconDrawable.java | 4 ++++ src/com/android/launcher3/Workspace.java | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java index bcb59c448d..45e4b2c4ad 100644 --- a/src/com/android/launcher3/PreloadIconDrawable.java +++ b/src/com/android/launcher3/PreloadIconDrawable.java @@ -213,6 +213,10 @@ class PreloadIconDrawable extends Drawable { return mAnimationProgress; } + public boolean hasNotCompleted() { + return mAnimationProgress < ANIMATION_PROGRESS_COMPLETED; + } + @Override public int getIntrinsicHeight() { return mIcon.getIntrinsicHeight(); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 17e59a6d70..e83ef7c2bb 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4334,8 +4334,9 @@ public class Workspace extends PagedView updates.contains(info)) { ShortcutInfo si = (ShortcutInfo) info; BubbleTextView shortcut = (BubbleTextView) v; - boolean oldPromiseState = getTextViewIcon(shortcut) - instanceof PreloadIconDrawable; + Drawable oldIcon = getTextViewIcon(shortcut); + boolean oldPromiseState = (oldIcon instanceof PreloadIconDrawable) + && ((PreloadIconDrawable) oldIcon).hasNotCompleted(); shortcut.applyFromShortcutInfo(si, mIconCache, si.isPromise() != oldPromiseState); From eea9fa4022b9cdc5b2f65bbd9ed6b314c883248d Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 11 Aug 2015 16:20:47 -0700 Subject: [PATCH 39/76] Ensuring that AllAppsGridAdapter is public. - Also show the search action when searching all apps Change-Id: I3470b0da565e6d1bce637ce09384d966215a4ca7 --- res/layout/all_apps_search_bar.xml | 2 +- src/com/android/launcher3/allapps/AllAppsGridAdapter.java | 2 +- .../android/launcher3/allapps/DefaultAppSearchController.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml index cf30eac364..c1399715d2 100644 --- a/res/layout/all_apps_search_bar.xml +++ b/res/layout/all_apps_search_bar.xml @@ -48,7 +48,7 @@ android:gravity="fill_horizontal|center_vertical" android:hint="@string/all_apps_search_bar_hint" android:inputType="text|textNoSuggestions|textCapWords" - android:imeOptions="actionDone|flagNoExtractUi" + android:imeOptions="actionSearch|flagNoExtractUi" android:maxLines="1" android:paddingLeft="8dp" android:scrollHorizontally="true" diff --git a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java index e96567c413..e613d50bcb 100644 --- a/src/com/android/launcher3/allapps/AllAppsGridAdapter.java +++ b/src/com/android/launcher3/allapps/AllAppsGridAdapter.java @@ -45,7 +45,7 @@ import java.util.List; /** * The grid view adapter of all the apps. */ -class AllAppsGridAdapter extends RecyclerView.Adapter { +public class AllAppsGridAdapter extends RecyclerView.Adapter { public static final String TAG = "AppsGridAdapter"; private static final boolean DEBUG = false; diff --git a/src/com/android/launcher3/allapps/DefaultAppSearchController.java b/src/com/android/launcher3/allapps/DefaultAppSearchController.java index 83b920589b..39b48aadab 100644 --- a/src/com/android/launcher3/allapps/DefaultAppSearchController.java +++ b/src/com/android/launcher3/allapps/DefaultAppSearchController.java @@ -166,7 +166,7 @@ final class DefaultAppSearchController extends AllAppsSearchBarController return false; } // Skip if it's not the right action - if (actionId != EditorInfo.IME_ACTION_DONE) { + if (actionId != EditorInfo.IME_ACTION_SEARCH) { return false; } // Skip if there isn't exactly one item From cc436d5d95af1aff9d486ce25f7965d159f871bb Mon Sep 17 00:00:00 2001 From: Vadim Tryshev Date: Tue, 11 Aug 2015 16:21:04 -0700 Subject: [PATCH 40/76] Fixing crash upon cancellation of an accessible drag. This fixes perhaps an old bug. If we started an accessible drag for an only item on a page, then uninstalled the app while dragging, the page was removed without unsetting its accessibility delegate. Later, the system asks the delegate to do something, but the drag is over, and some pointers are null, so everything crashes. Fixing this. Bug: 22028725 Change-Id: I85adcd42ae896603634994e20a7790792f7e84b1 (cherry picked from commit de1e67c38856ab1253426bfb4f892895eeb5f6d9) --- src/com/android/launcher3/Workspace.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index e83ef7c2bb..08e642948b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -887,6 +887,9 @@ public class Workspace extends PagedView } } + LauncherAccessibilityDelegate delegate = + LauncherAppState.getInstance().getAccessibilityDelegate(); + // We enforce at least one page to add new items to. In the case that we remove the last // such screen, we convert the last screen to the empty screen int minScreens = 1 + numCustomPages(); @@ -901,6 +904,11 @@ public class Workspace extends PagedView if (indexOfChild(cl) < currentPage) { pageShift++; } + + if (delegate != null && delegate.isInAccessibleDrag()) { + cl.enableAccessibleDrag(false, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); + } + removeView(cl); } else { // if this is the last non-custom content screen, convert it to the empty screen From 2d648b057fa2d6410b1472c1b9dd5b65cd9c9414 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 11 Aug 2015 13:56:28 -0700 Subject: [PATCH 41/76] Refreshing widget list when a package containing a widget changes Bug: 20698931 Change-Id: I50bafac1fc0a855edb1ace633a8a6f87a5c3040e --- .../launcher3/LauncherAppWidgetHost.java | 2 - src/com/android/launcher3/LauncherModel.java | 49 ++++++++++++++----- .../launcher3/WidgetPreviewLoader.java | 2 +- 3 files changed, 39 insertions(+), 14 deletions(-) diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index de7c610008..6c3a1e8963 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -95,8 +95,6 @@ public class LauncherAppWidgetHost extends AppWidgetHost { } protected void onProvidersChanged() { - mLauncher.getModel().loadAndBindWidgetsAndShortcuts(mLauncher, mLauncher, - true /* refresh */); if (!mProviderChangeListeners.isEmpty()) { for (Runnable callback : new ArrayList<>(mProviderChangeListeners)) { callback.run(); diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index f029561a32..b854c19731 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -17,6 +17,7 @@ package com.android.launcher3; import android.app.SearchManager; +import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -2865,8 +2866,7 @@ public class LauncherModel extends BroadcastReceiver // Cleanup any data stored for a deleted user. ManagedProfileHeuristic.processAllUsers(profiles, mContext); - loadAndBindWidgetsAndShortcuts(mApp.getContext(), tryGetCallbacks(oldCallbacks), - true /* refresh */); + loadAndBindWidgetsAndShortcuts(tryGetCallbacks(oldCallbacks), true /* refresh */); if (DEBUG_LOADERS) { Log.d(TAG, "Icons processed in " + (SystemClock.uptimeMillis() - loadTime) + "ms"); @@ -2935,7 +2935,7 @@ public class LauncherModel extends BroadcastReceiver } // Reload widget list. No need to refresh, as we only want to update the icons and labels. - loadAndBindWidgetsAndShortcuts(mApp.getContext(), callbacks, false); + loadAndBindWidgetsAndShortcuts(callbacks, false); } void enqueuePackageUpdated(PackageUpdatedTask task) { @@ -3277,8 +3277,36 @@ public class LauncherModel extends BroadcastReceiver }); } - // onProvidersChanged method (API >= 17) already refreshed the widget list - loadAndBindWidgetsAndShortcuts(context, callbacks, Build.VERSION.SDK_INT < 17); + // Update widgets + if (mOp == OP_ADD || mOp == OP_REMOVE || mOp == OP_UPDATE) { + // Always refresh for a package event on secondary user + boolean needToRefresh = !mUser.equals(UserHandleCompat.myUserHandle()); + + // Refresh widget list, if the package already had a widget. + synchronized (sBgLock) { + if (sBgWidgetProviders != null) { + HashSet pkgSet = new HashSet<>(); + Collections.addAll(pkgSet, mPackages); + + for (ComponentKey key : sBgWidgetProviders.keySet()) { + needToRefresh |= key.user.equals(mUser) && + pkgSet.contains(key.componentName.getPackageName()); + } + } + } + + if (!needToRefresh && mOp != OP_REMOVE) { + // Refresh widget list, if there is any newly added widget + PackageManager pm = context.getPackageManager(); + for (String pkg : mPackages) { + needToRefresh |= !pm.queryBroadcastReceivers( + new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE) + .setPackage(pkg), 0).isEmpty(); + } + } + + loadAndBindWidgetsAndShortcuts(callbacks, needToRefresh); + } // Write all the logs to disk mHandler.post(new Runnable() { @@ -3352,13 +3380,12 @@ public class LauncherModel extends BroadcastReceiver } } - public void loadAndBindWidgetsAndShortcuts(final Context context, final Callbacks callbacks, - final boolean refresh) { + public void loadAndBindWidgetsAndShortcuts(final Callbacks callbacks, final boolean refresh) { runOnWorkerThread(new Runnable() { @Override public void run() { - updateWidgetsModel(context, refresh); + updateWidgetsModel(refresh); final WidgetsModel model = mBgWidgetsModel.clone(); mHandler.post(new Runnable() { @@ -3382,10 +3409,10 @@ public class LauncherModel extends BroadcastReceiver * * @see #loadAndBindWidgetsAndShortcuts */ - @Thunk void updateWidgetsModel(Context context, boolean refresh) { - PackageManager packageManager = context.getPackageManager(); + @Thunk void updateWidgetsModel(boolean refresh) { + PackageManager packageManager = mApp.getContext().getPackageManager(); final ArrayList widgetsAndShortcuts = new ArrayList(); - widgetsAndShortcuts.addAll(getWidgetProviders(context, refresh)); + widgetsAndShortcuts.addAll(getWidgetProviders(mApp.getContext(), refresh)); Intent shortcutsIntent = new Intent(Intent.ACTION_CREATE_SHORTCUT); widgetsAndShortcuts.addAll(packageManager.queryIntentActivities(shortcutsIntent, 0)); mBgWidgetsModel.setWidgetsAndShortcuts(widgetsAndShortcuts); diff --git a/src/com/android/launcher3/WidgetPreviewLoader.java b/src/com/android/launcher3/WidgetPreviewLoader.java index 3d5058769c..346055566c 100644 --- a/src/com/android/launcher3/WidgetPreviewLoader.java +++ b/src/com/android/launcher3/WidgetPreviewLoader.java @@ -105,7 +105,7 @@ public class WidgetPreviewLoader { * sizes (landscape vs portrait). */ private static class CacheDb extends SQLiteOpenHelper { - private static final int DB_VERSION = 3; + private static final int DB_VERSION = 4; private static final String TABLE_NAME = "shortcut_and_widget_previews"; private static final String COLUMN_COMPONENT = "componentName"; From a09130b307796a7c2428cb3e2a250c0028ce51c8 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Wed, 12 Aug 2015 06:52:24 -0700 Subject: [PATCH 42/76] Import translations. DO NOT MERGE Change-Id: I6b0d7c6db67ef4de9a6ee0b7752cccf070223df4 Auto-generated-cl: translation import --- res/values-af/strings.xml | 1 + res/values-am/strings.xml | 1 + res/values-ar/strings.xml | 3 ++- res/values-az-rAZ/strings.xml | 1 + res/values-bg/strings.xml | 1 + res/values-bn-rBD/strings.xml | 1 + res/values-ca/strings.xml | 1 + res/values-cs/strings.xml | 1 + res/values-da/strings.xml | 1 + res/values-de/strings.xml | 1 + res/values-el/strings.xml | 1 + res/values-en-rAU/strings.xml | 1 + res/values-en-rGB/strings.xml | 1 + res/values-en-rIN/strings.xml | 1 + res/values-es-rUS/strings.xml | 1 + res/values-es/strings.xml | 1 + res/values-et-rEE/strings.xml | 1 + res/values-eu-rES/strings.xml | 1 + res/values-fa/strings.xml | 1 + res/values-fi/strings.xml | 1 + res/values-fr-rCA/strings.xml | 1 + res/values-fr/strings.xml | 1 + res/values-gl-rES/strings.xml | 1 + res/values-gu-rIN/strings.xml | 1 + res/values-hi/strings.xml | 1 + res/values-hr/strings.xml | 1 + res/values-hu/strings.xml | 1 + res/values-hy-rAM/strings.xml | 9 +++++---- res/values-in/strings.xml | 1 + res/values-is-rIS/strings.xml | 1 + res/values-it/strings.xml | 1 + res/values-iw/strings.xml | 1 + res/values-ja/strings.xml | 3 ++- res/values-ka-rGE/strings.xml | 2 ++ res/values-kk-rKZ/strings.xml | 1 + res/values-km-rKH/strings.xml | 1 + res/values-kn-rIN/strings.xml | 1 + res/values-ko/strings.xml | 1 + res/values-ky-rKG/strings.xml | 1 + res/values-lo-rLA/strings.xml | 1 + res/values-lt/strings.xml | 1 + res/values-lv/strings.xml | 1 + res/values-mk-rMK/strings.xml | 1 + res/values-ml-rIN/strings.xml | 1 + res/values-mn-rMN/strings.xml | 1 + res/values-mr-rIN/strings.xml | 1 + res/values-ms-rMY/strings.xml | 1 + res/values-my-rMM/strings.xml | 1 + res/values-nb/strings.xml | 1 + res/values-ne-rNP/strings.xml | 1 + res/values-nl/strings.xml | 1 + res/values-pa-rIN/strings.xml | 1 + res/values-pl/strings.xml | 1 + res/values-pt-rPT/strings.xml | 1 + res/values-pt/strings.xml | 1 + res/values-ro/strings.xml | 1 + res/values-ru/strings.xml | 1 + res/values-si-rLK/strings.xml | 1 + res/values-sk/strings.xml | 1 + res/values-sl/strings.xml | 1 + res/values-sq-rAL/strings.xml | 1 + res/values-sr/strings.xml | 1 + res/values-sv/strings.xml | 1 + res/values-sw/strings.xml | 1 + res/values-ta-rIN/strings.xml | 1 + res/values-te-rIN/strings.xml | 1 + res/values-th/strings.xml | 1 + res/values-tl/strings.xml | 1 + res/values-tr/strings.xml | 1 + res/values-uk/strings.xml | 1 + res/values-ur-rPK/strings.xml | 1 + res/values-uz-rUZ/strings.xml | 1 + res/values-vi/strings.xml | 1 + res/values-zh-rCN/strings.xml | 2 ++ res/values-zh-rHK/strings.xml | 1 + res/values-zh-rTW/strings.xml | 1 + res/values-zu/strings.xml | 1 + 77 files changed, 85 insertions(+), 6 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 7004524634..78be3f4218 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -32,6 +32,7 @@ "Deursoek programme" "Laai tans programme …" "Geen programme gevind wat met \"%1$s\" ooreenstem nie" + "Gaan na %1$s" "Niks meer spasie op die tuisskerm nie." "Geen plek meer in die Gunstelinge-laai nie" "Programme" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index d7fed9de4e..b5d982ee42 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -32,6 +32,7 @@ "መተግበሪያዎችን ይፈልጉ" "መተግበሪያዎችን በመጫን ላይ..." "ከ«%1$s» ጋር የሚዛመዱ ምንም መተግበሪያዎች አልተገኙም" + "ወደ %1$s ሂድ" "በዚህ መነሻ ማያ ገጽ ላይ ምንም ቦታ የለም።" "በተወዳጆች መሣቢያ ውስጥ ተጨማሪ ቦታ የለም" "መተግበሪያዎች" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index b7cd901de3..619e11d679 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -32,12 +32,13 @@ "البحث في التطبيقات" "جارٍ تحميل التطبيقات…" "لم يتم العثور على أية تطبيقات تتطابق مع \"%1$s\"" + "الانتقال إلى %1$s" "ليس هناك مساحة أخرى في هذه الشاشة الرئيسية." "لا يوجد المزيد من الحقول في علبة المفضلة" "التطبيقات" "الرئيسية" "إزالة" - "إزالة" + "إلغاء التثبيت" "معلومات عن التطبيق" "تثبيت اختصارات" "للسماح لتطبيق ما بإضافة اختصارات بدون تدخل المستخدم." diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml index 099a2208d8..b9ea83f263 100644 --- a/res/values-az-rAZ/strings.xml +++ b/res/values-az-rAZ/strings.xml @@ -32,6 +32,7 @@ "Tətbiq Axtarın" "Tətbiqlər endirilir..." "\"%1$s\" sorğusuna uyğun Tətbiqlər tapılmadı" + "%1$s daxil olun" "Bu Əsas ekranda boş yer yoxdur." "Favoritlər-də yer yoxdur" "Tətbiqlər" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index eeaa1ede47..214c64b2d1 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -32,6 +32,7 @@ "Търсене в приложенията" "Приложенията се зареждат…" "Няма намерени приложения, съответстващи на „%1$s“" + "Отваряне на %1$s" "На този начален екран няма повече място." "Няма повече място в областта с любимите" "Приложения" diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml index 57f7b511dd..129d2507a1 100644 --- a/res/values-bn-rBD/strings.xml +++ b/res/values-bn-rBD/strings.xml @@ -32,6 +32,7 @@ "অ্যাপ্লিকেশানগুলি অনুসন্ধান করুন" "অ্যাপ্লিকেশানগুলি লোড হচ্ছে..." "\"%1$s\" এর সাথে মেলে এমন কোনো অ্যাপ্লিকেশান পাওয়া যায়নি" + "%1$s এ যান" "এই হোম স্ক্রীনে আর কোনো জায়গা নেই৷" "পছন্দসই ট্রে-তে আর কোনো জায়গা নেই" "অ্যাপ্লিকেশানগুলি" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 83f284a9d4..cf874a4518 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -32,6 +32,7 @@ "Cerca a les aplicacions" "S\'estan carregant les aplicacions..." "No s\'ha trobat cap aplicació que coincideixi amb %1$s" + "Vés a %1$s" "Ja no queda espai en aquesta pantalla d\'inici." "No hi ha més espai a la safata Preferits." "Aplicacions" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index c99a7ed840..c2f9f421bd 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -32,6 +32,7 @@ "Hledat aplikace" "Načítání aplikací…" "Dotazu „%1$s“ neodpovídají žádné aplikace" + "Přejít na %1$s" "Na této ploše již není místo." "Na panelu Oblíbené položky již není místo." "Aplikace" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index eeab92e6ec..a9487050e2 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -32,6 +32,7 @@ "Søg i Apps" "Indlæser apps…" "Der blev ikke fundet nogen apps, som matcher \"%1$s\"" + "Gå til %1$s" "Der er ikke mere plads på denne startskærm." "Der er ikke mere plads i bakken Foretrukne" "Apps" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index ac6f76b0c7..96ae74b11b 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -32,6 +32,7 @@ "In Apps suchen" "Apps werden geladen..." "Keine Apps für \"%1$s\" gefunden" + "Gehe zu %1$s" "Auf diesem Startbildschirm ist kein Platz mehr vorhanden." "Ablage \"Favoriten\" ist voll." "Apps" diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index a60458a973..81b43e2e9f 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -32,6 +32,7 @@ "Αναζήτηση εφαρμογών" "Φόρτωση εφαρμογών…" "Δεν βρέθηκαν εφαρμογές για το ερώτημα \"%1$s\"" + "Μετάβαση σε %1$s" "Δεν υπάρχει χώρος σε αυτήν την αρχική οθόνη." "Δεν υπάρχει επιπλέον χώρος στην περιοχή Αγαπημένα" "Εφαρμογές" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 09963e422d..f2a6eab168 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -32,6 +32,7 @@ "Search Apps" "Loading Apps…" "No Apps found matching \"%1$s\"" + "Go to %1$s" "No more room on this Home screen." "No more room in the Favourites tray" "Apps" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 09963e422d..f2a6eab168 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -32,6 +32,7 @@ "Search Apps" "Loading Apps…" "No Apps found matching \"%1$s\"" + "Go to %1$s" "No more room on this Home screen." "No more room in the Favourites tray" "Apps" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 09963e422d..f2a6eab168 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -32,6 +32,7 @@ "Search Apps" "Loading Apps…" "No Apps found matching \"%1$s\"" + "Go to %1$s" "No more room on this Home screen." "No more room in the Favourites tray" "Apps" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index 636674293f..c754493c86 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -32,6 +32,7 @@ "Buscar aplicaciones" "Cargando aplicaciones…" "No hay aplicaciones que coincidan con %1$s." + "Ir a %1$s" "No hay más espacio en esta pantalla principal." "La bandeja de favoritos está llena." "Aplicaciones" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 34331fce93..16cd84fb2a 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -32,6 +32,7 @@ "Busca aplicaciones" "Cargando aplicaciones…" "No se han encontrado aplicaciones que contengan \"%1$s\"" + "Ir a %1$s" "No queda espacio en la pantalla de inicio." "La bandeja de favoritos está completa" "Aplicaciones" diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml index c4ba823cc1..84ca45af48 100644 --- a/res/values-et-rEE/strings.xml +++ b/res/values-et-rEE/strings.xml @@ -32,6 +32,7 @@ "Otsige rakendustest" "Rakenduste laadimine ..." "Päringule „%1$s” ei vastanud ükski rakendus" + "Mine: %1$s" "Sellel avaekraanil pole enam ruumi." "Salves Lemmikud pole rohkem ruumi" "Rakendused" diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml index 8bfca6cd1a..096a4f95e0 100644 --- a/res/values-eu-rES/strings.xml +++ b/res/values-eu-rES/strings.xml @@ -32,6 +32,7 @@ "Bilatu aplikazioetan" "Aplikazioak kargatzen…" "Ez da aurkitu \"%1$s\" bilaketarekin bat datorren aplikaziorik" + "Joan hona: %1$s" "Hasierako pantaila honetan ez dago toki gehiago." "Ez dago toki gehiago Gogokoak erretiluan" "Aplikazioak" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 701d59d50d..3b6d01ae6e 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -32,6 +32,7 @@ "جستجوی برنامه‌ها" "در حال بارگیری برنامه‌ها..." "هیچ برنامه‌ای مطابق با «%1$s» پیدا نشد" + "رفتن به %1$s" "فضای بیشتری در این صفحه اصلی موجود نیست." "فضای بیشتری در سینی موارد دلخواه وجود ندارد" "برنامه‌ها" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index f0766d68ab..47b580e3b6 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -32,6 +32,7 @@ "Sovellushaku" "Ladataan sovelluksia…" "”%1$s” ei palauttanut sovelluksia." + "Siirry: %1$s" "Tässä aloitusruudussa ei ole enää tilaa." "Suosikit-valikossa ei ole enää tilaa" "Sovellukset" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index 0c19ef2687..91271f7e97 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -32,6 +32,7 @@ "Rechercher des applications" "Chargement des applications en cours..." "Aucune application trouvée correspondant à « %1$s »" + "Aller à « %1$s »" "Pas d\'espace libre sur l\'écran d\'accueil." "Il n\'y a plus d\'espace dans la zone des favoris" "Applications" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 662bcf14e9..856690da8c 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -32,6 +32,7 @@ "Rechercher dans les applications" "Chargement des applications en cours…" "Aucune application ne correspond à la requête \"%1$s\"." + "Accéder au service %1$s" "Pas d\'espace libre sur cet écran d\'accueil." "Plus d\'espace disponible dans la zone de favoris." "Applications" diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml index 6cf76a7c0b..ccfe21ed1b 100644 --- a/res/values-gl-rES/strings.xml +++ b/res/values-gl-rES/strings.xml @@ -32,6 +32,7 @@ "Aplicacións de busca" "Cargando aplicacións..." "Non se atoparon aplicacións que coincidan con \"%1$s\"" + "Ir a %1$s" "Non hai máis espazo nesta pantalla de inicio." "Non hai máis espazo na bandexa de favoritos" "Aplicacións" diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml index 4a2274f1b6..db056a0e8c 100644 --- a/res/values-gu-rIN/strings.xml +++ b/res/values-gu-rIN/strings.xml @@ -32,6 +32,7 @@ "શોધ એપ્લિકેશનો" "એપ્લિકેશનો લોડ કરી રહ્યું છે…" "\"%1$s\" થી મેળ ખાતી કોઈ એપ્લિકેશનો મળી નથી" + "%1$s પર જાઓ" "આ હોમ સ્ક્રીન પર વધુ જગ્યા નથી." "મનપસંદ ટ્રે પર વધુ જગ્યા નથી" "એપ્લિકેશનો" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 6fdbf24411..7cf4f33ef7 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -32,6 +32,7 @@ "ऐप्‍स खोजें" "ऐप्स लोड हो रहे हैं..." "\"%1$s\" से मिलान करने वाला कोई ऐप नहीं मिला" + "%1$s पर जाएं" "इस होम स्‍क्रीन पर स्थान शेष नहीं है." "पसंदीदा ट्रे में और स्थान नहीं है" "ऐप्लिकेशन" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index a54a7e4b02..2679570dcf 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -32,6 +32,7 @@ "Pretraži aplikacije" "Učitavanje aplikacija…" "Nema aplikacija podudarnih s upitom \"%1$s\"" + "Idite na %1$s" "Na ovom početnom zaslonu više nema mjesta." "Nema više prostora na traci Favoriti" "Aplikacije" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index e9fc5747fd..41c38b2def 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -32,6 +32,7 @@ "Alkalmazások keresése" "Alkalmazások betöltése…" "Egy alkalmazás sem található a(z) „%1$s” lekérdezésre." + "Keresse fel ezt: %1$s" "Nincs több hely ezen a kezdőképernyőn." "Nincs több hely a Kedvencek tálcán" "Alkalmazások" diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml index de4aed1583..49a9d18ab4 100644 --- a/res/values-hy-rAM/strings.xml +++ b/res/values-hy-rAM/strings.xml @@ -32,6 +32,7 @@ "Հավելվածների որոնում" "Հավելվածների բեռնում…" %1$s» հարցմանը համապատասխանող հավելվածներ չեն գտնվել" + "Գնալ %1$s" "Այլևս տեղ չկա այս հիմնական էկրանին:" "Ընտրյալների ցուցակում այլևս ազատ տեղ չկա" "Ծրագրեր" @@ -59,11 +60,11 @@ "Պաստառներ, վիջեթներ և կարգավորումներ" "Հարմարեցնելու համար հպեք և պահեք հետնաշերտի վրա" "ՀԱՍԿԱՆԱԼԻ Է" - "Թղթապանակը բաց է, %1$d-ից %2$d" + "Պանակը բաց է, %1$d-ից %2$d" "Հպեք՝ պանակը փակելու համար" "Հպեք՝ վերանվանումը պահելու համար" - "Թղթապանակը փակ է" - "Թղթապանակը վերանվանվեց %1$s" + "Պանակը փակ է" + "Պանակը վերանվանվեց %1$s" "Թղթապանակ՝ %1$s" "Վիջեթներ" "Պաստառներ" @@ -87,7 +88,7 @@ "Ավելացնել «%1$s» պանակին" "Տարրն ավելացվեց թղթապանակում" "Ստեղծել թղթապանակ, օգտագործելով՝ %1$s" - "Թղթապանակը ստեղծվեց" + "Պանակը ստեղծվեց" "Տեղափոխել Հիմնական էկրան" "Տեղափոխել էկրանը ձախ" "Տեղափոխել էկրանը աջ" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index c65254a2a0..21c2080562 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -32,6 +32,7 @@ "Telusuri Apps" "Memuat Aplikasi..." "Tidak ditemukan Aplikasi yang cocok dengan \"%1$s\"" + "Buka %1$s" "Tidak ada ruang lagi pada layar Utama ini." "Tidak ada ruang tersisa di baki Favorit" "Aplikasi" diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml index 1d46cdc93e..7422eaf582 100644 --- a/res/values-is-rIS/strings.xml +++ b/res/values-is-rIS/strings.xml @@ -32,6 +32,7 @@ "Leita í forritum" "Hleður forrit…" "Ekki fundust forrit sem samsvara „%1$s“" + "Fara í %1$s" "Ekki meira pláss á þessum heimaskjá." "Ekki meira pláss í bakka fyrir uppáhald" "Forrit" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 02057a19c5..e1e75b37f6 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -32,6 +32,7 @@ "Cerca app" "Caricamento di app…" "Nessuna app trovata corrispondente a \"%1$s\"" + "Vai a %1$s" "Spazio nella schermata Home esaurito." "Spazio esaurito nella barra dei Preferiti" "App" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index a8039f7c18..3686abdbb9 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -32,6 +32,7 @@ "חפש אפליקציות" "טוען אפליקציות…" "לא נמצאו אפליקציות התואמות ל-\"%1$s\"" + "עבור אל %1$s" "אין עוד מקום במסך דף הבית הזה." "אין עוד מקום במגש המועדפים" "אפליקציות" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index ba58d88ef6..87b3fa4992 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -32,6 +32,7 @@ "アプリを検索" "アプリを読み込んでいます…" "「%1$s」に一致するアプリは見つかりませんでした" + "%1$sにアクセス" "このホーム画面に空きスペースがありません。" "お気に入りトレイに空きスペースがありません" "アプリ" @@ -57,7 +58,7 @@ "アイコンをコピー" "初期状態にリセットする" "壁紙、ウィジェット、設定" - "カスタマイズするにはバックグラウンドを押し続けます" + "カスタマイズするには背景を押し続けます" "OK" "フォルダが開いています。%1$dx%2$dの大きさです" "タップしてフォルダを閉じます" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 07030d866e..46a2343556 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -32,6 +32,8 @@ "აპების ძიება" "აპები იტვირთება..." "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" + + "ამ მთავარ ეკრანზე ადგილი აღარ არის." "რჩეულების თაროზე ადგილი არ არის" "აპები" diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml index e79c9ce987..cae5e5edf9 100644 --- a/res/values-kk-rKZ/strings.xml +++ b/res/values-kk-rKZ/strings.xml @@ -32,6 +32,7 @@ "Қолданбаларды іздеу" "Қолданбалар жүктелуде…" %1$s» сұрауына сәйкес келетін қолданбалар жоқ" + "%1$s сұрауына өту" "Бұл Негізгі экранда орын қалмады." "Қалаулылар науасында орын қалмады" "Қолданбалар" diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml index 9f7f53717b..5ffbf76160 100644 --- a/res/values-km-rKH/strings.xml +++ b/res/values-km-rKH/strings.xml @@ -32,6 +32,7 @@ "ស្វែងរកកម្មវិធី" "កំពុងដំណើរការកម្មវិធី..." "គ្មានកម្មវិធីដែលត្រូវជាមួយ \"%1$s\" ទេ" + "ចូលទៅកាន់ %1$s" "គ្មាន​បន្ទប់​នៅ​លើ​អេក្រង់​ដើម​នេះ​ទៀត​ទេ។" "គ្មាន​បន្ទប់​​ក្នុង​ថាស​និយម​ប្រើ" "កម្មវិធី" diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml index 5cd8792dd4..a1aab7b5cc 100644 --- a/res/values-kn-rIN/strings.xml +++ b/res/values-kn-rIN/strings.xml @@ -32,6 +32,7 @@ "ಅಪ್ಲಿಕೇಷನ್‌ಗಳನ್ನು ಹುಡುಕಿ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳನ್ನು ಲೋಡ್ ಮಾಡಲಾಗುತ್ತಿದೆ..." "\"%1$s\" ಹೊಂದಿಕೆಯ ಯಾವುದೇ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಕಂಡುಬಂದಿಲ್ಲ" + "%1$s ಗೆ ಹೋಗಿ" "ಈ ಮುಖಪುಟದ ಪರದೆಯಲ್ಲಿ ಹೆಚ್ಚು ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ." "ಮೆಚ್ಚಿನವುಗಳ ಟ್ರೇನಲ್ಲಿ ಹೆಚ್ಚಿನ ಸ್ಥಳಾವಕಾಶವಿಲ್ಲ" "ಅಪ್ಲಿಕೇಶನ್‌ಗಳು" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index fa445acca2..e0697121a0 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -32,6 +32,7 @@ "앱 검색" "앱 로드 중..." "\'%1$s\'와(과) 일치하는 앱이 없습니다." + "%1$s(으)로 이동" "홈 화면에 더 이상 공간이 없습니다." "즐겨찾기 트레이에 더 이상 공간이 없습니다." "앱" diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml index 20787489e5..c530b5c9fc 100644 --- a/res/values-ky-rKG/strings.xml +++ b/res/values-ky-rKG/strings.xml @@ -32,6 +32,7 @@ "Колдонмолорду издөө" "Колдонмолор жүктөлүүдө…" "\"%1$s\" дал келген колдонмолор табылган жок" + "%1$s сурамына өтүңүз" "Бул Үй экранында бош орун жок." "Тандамалдар тайпасында орун калган жок" "Колдонмолор" diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml index dff2612bab..005f0265fc 100644 --- a/res/values-lo-rLA/strings.xml +++ b/res/values-lo-rLA/strings.xml @@ -32,6 +32,7 @@ "ຊອກຫາແອັບ" "​ກຳ​ລັງ​ໂຫລດ​ແອັບ..." "ບໍ່​ພົບ​ແອັບ​ໃດ​ທີ່​ກົງ​ກັນ \"%1$s\"" + "ໄປ​ທີ່ %1$s" "ບໍ່ມີຫ້ອງເຫຼືອໃນໜ້າຈໍຫຼັກນີ້." "ບໍ່ມີບ່ອນຫວ່າງໃນຖາດສຳລັບເກັບສິ່ງທີ່ໃຊ້ເປັນປະຈຳ" "ແອັບຯ" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index b5c0832dcb..442874dbe8 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -32,6 +32,7 @@ "Ieškoti programų" "Įkeliamos programos..." "Nerasta jokių užklausą „%1$s“ atitinkančių programų" + "Eiti į %1$s" "Šiame pagrindiniame ekrane vietos nebėra." "Mėgstamiausių dėkle nebėra vietos" "Programos" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 432bda4439..1bef22e039 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -32,6 +32,7 @@ "Meklēt lietotnes" "Notiek lietotņu ielāde…" "Vaicājumam “%1$s” neatbilda neviena lietotne." + "Doties uz: %1$s" "Šajā sākuma ekrānā vairs nav vietas." "Izlases joslā vairs nav vietas." "Lietotnes" diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml index 8c276e3f06..0a6704b50c 100644 --- a/res/values-mk-rMK/strings.xml +++ b/res/values-mk-rMK/strings.xml @@ -32,6 +32,7 @@ "Пребарување апликации" "Се вчитуваат апликации…" "Не се најдени апликации што одговараат на „%1$s“" + "Оди на %1$s" "Нема повеќе простор на овој екран на почетната страница." "Нема повеќе простор на лентата „Омилени“" "Апликации" diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml index 59ad153a85..2bd80d3584 100644 --- a/res/values-ml-rIN/strings.xml +++ b/res/values-ml-rIN/strings.xml @@ -32,6 +32,7 @@ "ആപ്പ്‌സ് തിരയുക" "ആപ്പ്‌സ് ലോഡുചെയ്യുന്നു..." "\"%1$s\" എന്നതുമായി പൊരുത്തപ്പെടുന്ന ആപ്പ്‌സൊന്നും കണ്ടെത്തിയില്ല" + "%1$s എന്നതിലേക്ക് പോവുക" "ഈ ഹോം സ്‌ക്രീനിൽ ഒഴിവൊന്നുമില്ല." "പ്രിയപ്പെട്ടവയുടെ ട്രേയിൽ ഒഴിവൊന്നുമില്ല" "അപ്ലിക്കേഷനുകൾ" diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml index fafb37b2ce..19ba66fdb3 100644 --- a/res/values-mn-rMN/strings.xml +++ b/res/values-mn-rMN/strings.xml @@ -32,6 +32,7 @@ "Апп хайх" "Аппликейшныг ачаалж байна..." "\"%1$s\"-тай нийцэх апп олдсонгүй" + "%1$s руу очих" "Энэ Нүүр дэлгэц зайгүй." "\"Дуртай\" трей дээр өөр зай байхгүй байна" "Апп" diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml index 657010d6cd..dc29b54628 100644 --- a/res/values-mr-rIN/strings.xml +++ b/res/values-mr-rIN/strings.xml @@ -32,6 +32,7 @@ "अॅप्स शोधा" "अॅप्स लोड करीत आहे..." "\"%1$s\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत" + "%1$s वर जा" "या मुख्य स्क्रीनवर आणखी जागा नाही." "आवडीच्या ट्रे मध्ये आणखी जागा नाही" "अॅप्स" diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml index 0c6a5f501a..26f27488c6 100644 --- a/res/values-ms-rMY/strings.xml +++ b/res/values-ms-rMY/strings.xml @@ -32,6 +32,7 @@ "Cari Apl" "Memuatkan Apl…" "Tiada Apl yang ditemui sepadan dengan \"%1$s\"" + "Pergi ke %1$s" "Tiada lagi ruang pada skrin Laman Utama ini." "Tiada ruang dalam dulang Kegemaran lagi" "Apl" diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml index 19ec3a6ffd..c13ff7e0d9 100644 --- a/res/values-my-rMM/strings.xml +++ b/res/values-my-rMM/strings.xml @@ -32,6 +32,7 @@ "ရှာဖွေမှု Appများ" "App များ ရယူနေစဉ်..." "\"%1$s\" နှင့်ကိုက်ညီသည့် အပ်ဖ်များမတွေ့ပါ" + "%1$sကို သွားပါ" "ဤပင်မမျက်နှာစာတွင် နေရာလွတ် မကျန်တော့ပါ" "အနှစ်သက်ဆုံးများ ထားရာတွင် နေရာလွတ် မကျန်တော့ပါ" "အပ်ပလီကေးရှင်းများ" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 21b87c894e..198d56c213 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -32,6 +32,7 @@ "Søk i apper" "Laster inn apper …" "Fant ingen apper som samsvarer med «%1$s»" + "Gå til %1$s" "Denne startsiden er full." "Favoritter-skuffen er full" "Apper" diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml index dd3b3f3102..472bd4f5d1 100644 --- a/res/values-ne-rNP/strings.xml +++ b/res/values-ne-rNP/strings.xml @@ -32,6 +32,7 @@ "अनुप्रयोगहरू खोज्नुहोस्" "अनुप्रयोगहरू लोड गरिँदै..." "\"%1$s\" सँग मिल्दो कुनै अनुप्रयोगहरू फेला परेनन्" + "%1$s मा जानुहोस्" "यो गृह स्क्रिनमा कुनै थप ठाउँ छैन।" "मनपर्ने ट्रे अब कुनै ठाँउ छैन" "अनुप्रयोगहरू" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index fcbe375e75..6f4981087a 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -32,6 +32,7 @@ "Apps zoeken" "Apps laden…" "Er zijn geen apps gevonden die overeenkomen met \'%1$s\'" + "Ga naar %1$s" "Er is geen ruimte meer op dit startscherm." "Geen ruimte meer in het vak \'Favorieten\'" "Apps" diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml index d510dc7fa2..30c44280e0 100644 --- a/res/values-pa-rIN/strings.xml +++ b/res/values-pa-rIN/strings.xml @@ -32,6 +32,7 @@ "ਐਪਸ ਖੋਜੋ" "ਐਪਸ ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ..." "\"%1$s\" ਨਾਲ ਮਿਲਦੀ ਕੋਈ ਵੀ ਐਪਸ ਨਹੀਂ ਲੱਭੀਆਂ" + "%1$s ਤੇ ਜਾਓ" "ਇਸ ਹੋਮ ਸਕ੍ਰੀਨ ਲਈ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ ਹੈ।" "ਮਨਪਸੰਦ ਟ੍ਰੇ ਵਿੱਚ ਹੋਰ ਖਾਲੀ ਸਥਾਨ ਨਹੀਂ।" "ਐਪਸ" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index b3b761d9ca..a1fd29f56b 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -32,6 +32,7 @@ "Szukaj w aplikacjach" "Wczytuję aplikacje…" "Nie znaleziono aplikacji pasujących do zapytania „%1$s”" + "Otwórz %1$s" "Brak miejsca na tym ekranie głównym." "Brak miejsca w Ulubionych" "Aplikacje" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 9140b62f2d..0d8a431799 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -32,6 +32,7 @@ "Pesquisar aplicações" "A carregar aplicações..." "Não foram encontradas aplic. que correspondam a \"%1$s\"" + "Aceder a %1$s" "Sem espaço suficiente neste Ecrã principal." "Não existe mais espaço no tabuleiro de Favoritos" "Aplicações" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index c8e6458497..41e5c021e9 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -32,6 +32,7 @@ "Pesquisar apps" "Carregando apps…" "Nenhum app encontrado que corresponda a \"%1$s\"" + "Ir para %1$s" "Não há mais espaço na tela inicial." "Sem espaço na bandeja de favoritos" "Apps" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 88cbc79fc5..ffaca78569 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -32,6 +32,7 @@ "Căutați aplicații" "Se încarcă aplicațiile..." "Nu s-a găsit nicio aplicație pentru „%1$s”" + "Accesați %1$s" "Nu mai este loc pe acest Ecran de pornire." "Spațiu epuizat în bara Preferate" "Aplicații" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 5b492fe127..6e69e4dee7 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -32,6 +32,7 @@ "Поиск приложений" "Загрузка…" "По запросу \"%1$s\" ничего не найдено" + "%1$s" "На этом экране все занято" "В разделе \"Избранное\" больше нет места" "Приложения" diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml index 68e3178ae2..476fff8167 100644 --- a/res/values-si-rLK/strings.xml +++ b/res/values-si-rLK/strings.xml @@ -32,6 +32,7 @@ "යෙදුම් සෙවීම" "යෙදුම් පූරණය වෙමින්…" "\"%1$s\" සමග ගැළපෙන යෙදුම් හමු නොවිණි" + "%1$s වෙත යන්න" "මෙම මුල් පිටු තිරය මත තවත් අවසර නැත." "ප්‍රියතම දෑ ඇති තැටියේ තවත් ඉඩ නොමැත" "යෙදුම්" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 1cb76e3a8d..703b46cb44 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -32,6 +32,7 @@ "Vyhľadávanie v aplikáciách" "Načítavajú sa aplikácie..." "Nenašli sa žiadne aplikácie zodpovedajúce dopytu %1$s" + "Prejsť na dopyt %1$s" "Na tejto ploche už nie je miesto" "Na paneli Obľúbené položky už nie je miesto" "Aplikácie" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 92c69a562e..497378b788 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -32,6 +32,7 @@ "Iskanje po aplikacijah" "Nalaganje aplikacij …" "Ni aplikacij, ki bi ustrezale poizvedbi »%1$s«" + "Odpri storitev %1$s" "Na tem začetnem zaslonu ni več prostora." "V vrstici za priljubljene ni več prostora" "Aplikacije" diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml index 65aabf6899..2b535a3a5f 100644 --- a/res/values-sq-rAL/strings.xml +++ b/res/values-sq-rAL/strings.xml @@ -32,6 +32,7 @@ "Kërko për aplikacione" "Po ngarkon aplikacionet..." "Nuk u gjet asnjë aplikacion që përputhet me \"%1$s\"" + "Shko te %1$s" "Nuk ka më hapësirë në këtë ekran bazë." "Nuk ka më hapësirë në tabakanë \"Të preferuarat\"" "Aplikacionet" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 0f93bf3ea5..f21aae0198 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -32,6 +32,7 @@ "Претражите апликације" "Апликације се учитавају..." "Није пронађена ниједна апликација за „%1$s“" + "Иди на апликацију %1$s" "Нема више простора на овом почетном екрану." "Нема више простора на траци Омиљено" "Апликације" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 93fce80cac..385d7a6f64 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -32,6 +32,7 @@ "Sök efter appar" "Läser in appar …" "Det gick inte att hitta några appar som matchar %1$s" + "Gå till %1$s" "Det finns inte plats för mer på den här startskärmen." "Favoritfältet är fullt" "Appar" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 297b731bfb..b86fb7325e 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -32,6 +32,7 @@ "Tafuta Programu" "Inapakia Programu..." "Haikupata programu zinazolingana na \"%1$s\"" + "Nenda kwenye %1$s" "Hakuna nafasi katika skrini hii ya Mwanzo." "Hakuna nafasi zaidi katika treya ya Vipendeleo" "Programu" diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml index 1f2a6f9593..9dac6d011a 100644 --- a/res/values-ta-rIN/strings.xml +++ b/res/values-ta-rIN/strings.xml @@ -32,6 +32,7 @@ "பயன்பாடுகளில் தேடுக" "பயன்பாடுகளை ஏற்றுகிறது..." "\"%1$s\" உடன் பொருந்தும் பயன்பாடுகள் இல்லை" + "%1$sக்குச் செல்லவும்" "முகப்புத் திரையில் இடமில்லை." "பிடித்தவை ட்ரேயில் இடமில்லை" "பயன்பாடுகள்" diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml index bd5fd70db7..32cb292a1a 100644 --- a/res/values-te-rIN/strings.xml +++ b/res/values-te-rIN/strings.xml @@ -32,6 +32,7 @@ "అనువర్తనాలను శోధించండి" "అనువర్తనాలను లోడ్ చేస్తోంది…" "\"%1$s\"కి సరిపోలే అనువర్తనాలేవీ కనుగొనబడలేదు" + "%1$sకి వెళ్లు" "ఈ హోమ్ స్క్రీన్‌లో ఖాళీ లేదు." "ఇష్టమైనవి ట్రేలో ఖాళీ లేదు" "అనువర్తనాలు" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index 85919af9a1..53ae5896b1 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -32,6 +32,7 @@ "ค้นหาแอป" "กำลังโหลดแอป…" "ไม่พบแอปที่ตรงกับ \"%1$s\"" + "ไปที่ %1$s" "ไม่มีที่ว่างในหน้าจอหลักนี้" "ไม่มีพื้นที่เหลือในถาดรายการโปรด" "แอป" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 69a5bb7d92..43afeab530 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -32,6 +32,7 @@ "Mga App sa Paghahanap" "Nilo-load ang Mga App…" "Walang nakitang Mga App na tumutugma sa \"%1$s\"" + "Pumunta sa %1$s" "Wala nang lugar sa Home screen na ito." "Wala nang lugar sa tray ng Mga Paborito" "Apps" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index d933c87703..064d0496a0 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -32,6 +32,7 @@ "Uygulamalarda Ara" "Uygulamalar Yükleniyor…" "\"%1$s\" ile eşleşen uygulama bulunamadı" + "%1$s uygulamasına git" "Bu Ana ekranda yer kalmadı." "Favoriler tepsisinde başka yer kalmadı" "Uygulamalar" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index b06177e712..ff9364e5f4 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -32,6 +32,7 @@ "Пошук додатків" "Завантаження додатків…" "Немає додатків для запиту \"%1$s\"" + "Перейти в додаток %1$s" "На цьому головному екрані більше немає місця." "В області \"Вибране\" немає місця" "Додатки" diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml index ba189c87da..f3d631173c 100644 --- a/res/values-ur-rPK/strings.xml +++ b/res/values-ur-rPK/strings.xml @@ -32,6 +32,7 @@ "ایپس تلاش کریں" "ایپس لوڈ ہو رہی ہیں…" "\"%1$s\" سے مماثل کوئی ایپس نہیں ملیں" + "%1$s پر جائیں" "اس ہوم اسکرین پر مزید کوئی گنجائش نہیں ہے۔" "پسندیدہ ٹرے میں مزید کوئی گنجائش نہیں ہے" "ایپس" diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml index c3bffeb3c2..24e4c4d14d 100644 --- a/res/values-uz-rUZ/strings.xml +++ b/res/values-uz-rUZ/strings.xml @@ -32,6 +32,7 @@ "Ilovalarni qidirish" "Ilovalar yuklanmoqda…" "“%1$s” bilan mos hech qanday ilova topilmadi" + "O‘tish: %1$s" "Uy ekranida bitta ham xona yo‘q." "Ajratilganlarda birorta ham xona yo‘q" "Ilovalar" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index f57f491ae6..6eff50760f 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -32,6 +32,7 @@ "Tìm kiếm ứng dụng" "Đang tải ứng dụng..." "Không tìm thấy ứng dụng nào phù hợp với \"%1$s\"" + "Chuyển tới %1$s" "Không còn chỗ trên Màn hình chính này." "Không còn chỗ trong khay Mục yêu thích" "Ứng dụng" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 82274ba2c1..495a14b37a 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -32,6 +32,8 @@ "搜索应用" "正在加载应用…" "未找到与“%1$s”相符的应用" + + "此主屏幕上已没有空间。" "收藏栏已满" "应用" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index f7d240e053..62032ecaa5 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -32,6 +32,7 @@ "搜尋應用程式" "正在載入應用程式…" "無法找到與「%1$s」相符的應用程式" + "前往 %1$s" "主畫面已無空間。" "我的收藏寄存區沒有足夠空間" "應用程式" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 39a0ec77ae..0b3f4d6bc5 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -32,6 +32,7 @@ "搜尋應用程式" "正在載入應用程式…" "找不到符合「%1$s」的應用程式" + "前往 %1$s" "這個主螢幕已無空間。" "「我的最愛」匣已無可用空間" "應用程式" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 83ba089df5..bb49f1f0ad 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -32,6 +32,7 @@ "Sesha Izinhlelo Zokusebenza" "Ilayisha izinhlelo zokusebenza..." "Azikho izinhlelo zokusebenza ezitholakele ezifana ne-\"%1$s\"" + "Hamba ku-%1$s" "Asisekho isikhala kulesi sikrini Sasekhaya." "Asisekho isikhala kwitreyi lezintandokazi" "Izinhlelo zokusebenza" From 79a640ec354e61363e0c347793f70b3a285a0240 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 12 Aug 2015 15:28:33 -0700 Subject: [PATCH 43/76] Ensure that crop view is loaded before applying a wallpaper Bug: 22293299 Change-Id: I64d5268cc263bbb6595a0b28bdd5ec73d540da41 --- .../src/com/android/launcher3/WallpaperPickerActivity.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 069638f8cd..75d8745529 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -550,7 +550,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { new View.OnClickListener() { @Override public void onClick(View v) { - if (mSelectedTile != null) { + // Ensure that a tile is slelected and loaded. + if (mSelectedTile != null && mCropView.getTileSource() != null) { // Prevent user from selecting any new tile. mWallpaperStrip.setVisibility(View.GONE); actionBar.hide(); From d106418dee16b041f30682540fbd3ca3d841b7af Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 13 Aug 2015 12:08:30 -0700 Subject: [PATCH 44/76] Reloading launcher whenever there is an external update to contentprovider, irrespective of the uri Change-Id: If1a06da57171be30adb74f09ac256a8b03ab8549 --- .../android/launcher3/LauncherProvider.java | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 6cbb267bed..0599714057 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -77,8 +77,6 @@ public class LauncherProvider extends ContentProvider { static final String TABLE_WORKSPACE_SCREENS = LauncherSettings.WorkspaceScreens.TABLE_NAME; static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED"; - private static final String URI_PARAM_IS_EXTERNAL_ADD = "isExternalAdd"; - private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name"; @Thunk LauncherProviderChangeListener mListener; @@ -140,14 +138,21 @@ public class LauncherProvider extends ContentProvider { return db.insert(table, nullColumnHack, values); } + private void reloadLauncherIfExternal() { + if (Binder.getCallingPid() != Process.myPid()) { + LauncherAppState app = LauncherAppState.getInstanceNoCreate(); + if (app != null) { + app.reloadWorkspace(); + } + } + } + @Override public Uri insert(Uri uri, ContentValues initialValues) { SqlArguments args = new SqlArguments(uri); - // In very limited cases, we support system|signature permission apps to add to the db - String externalAdd = uri.getQueryParameter(URI_PARAM_IS_EXTERNAL_ADD); - final boolean isExternalAll = externalAdd != null && "true".equals(externalAdd); - if (isExternalAll) { + // In very limited cases, we support system|signature permission apps to modify the db. + if (Binder.getCallingPid() != Process.myPid()) { if (!mOpenHelper.initializeExternalAdd(initialValues)) { return null; } @@ -161,13 +166,7 @@ public class LauncherProvider extends ContentProvider { uri = ContentUris.withAppendedId(uri, rowId); notifyListeners(); - if (isExternalAll) { - LauncherAppState app = LauncherAppState.getInstanceNoCreate(); - if (app != null) { - app.reloadWorkspace(); - } - } - + reloadLauncherIfExternal(); return uri; } @@ -192,6 +191,7 @@ public class LauncherProvider extends ContentProvider { } notifyListeners(); + reloadLauncherIfExternal(); return values.length; } @@ -203,6 +203,7 @@ public class LauncherProvider extends ContentProvider { try { ContentProviderResult[] result = super.applyBatch(operations); db.setTransactionSuccessful(); + reloadLauncherIfExternal(); return result; } finally { db.endTransaction(); @@ -217,6 +218,7 @@ public class LauncherProvider extends ContentProvider { int count = db.delete(args.table, args.where, args.args); if (count > 0) notifyListeners(); + reloadLauncherIfExternal(); return count; } @@ -229,6 +231,7 @@ public class LauncherProvider extends ContentProvider { int count = db.update(args.table, values, args.where, args.args); if (count > 0) notifyListeners(); + reloadLauncherIfExternal(); return count; } From 97b0d08d92c64034ba03ae8da5a8531edbd60d52 Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 13 Aug 2015 15:18:25 -0700 Subject: [PATCH 45/76] Refactoring to ExtendedEditText. --- res/layout/all_apps_search_bar.xml | 2 +- res/layout/user_folder.xml | 2 +- ...rchEditView.java => ExtendedEditText.java} | 20 +++++------ src/com/android/launcher3/Folder.java | 13 +++++-- src/com/android/launcher3/FolderEditText.java | 36 ------------------- .../allapps/DefaultAppSearchController.java | 12 ++++--- 6 files changed, 30 insertions(+), 55 deletions(-) rename src/com/android/launcher3/{allapps/AllAppsSearchEditView.java => ExtendedEditText.java} (75%) delete mode 100644 src/com/android/launcher3/FolderEditText.java diff --git a/res/layout/all_apps_search_bar.xml b/res/layout/all_apps_search_bar.xml index 0c183eaee4..69a66c817f 100644 --- a/res/layout/all_apps_search_bar.xml +++ b/res/layout/all_apps_search_bar.xml @@ -38,7 +38,7 @@ android:contentDescription="@string/all_apps_button_label" android:src="@drawable/ic_arrow_back_grey" /> - - Date: Wed, 12 Aug 2015 15:12:16 -0700 Subject: [PATCH 46/76] Disabling auto restore of widgets. > Always show "Setup" button for a widget which has a config activity. Bug: 23155863 Change-Id: I60683889e76be830a76ac1245e079574aaf0ad10 --- .../launcher3/AppWidgetsRestoredReceiver.java | 4 +- src/com/android/launcher3/Launcher.java | 76 +++++++++++-------- src/com/android/launcher3/LauncherModel.java | 8 +- 3 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java index 5e7a012d21..b1d51ece04 100644 --- a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java +++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java @@ -13,6 +13,7 @@ import android.os.AsyncTask; import android.util.Log; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.compat.AppWidgetManagerCompat; import java.util.ArrayList; import java.util.List; @@ -48,7 +49,8 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver { final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]); final int state; if (LauncherModel.isValidProvider(provider)) { - state = LauncherAppWidgetInfo.RESTORE_COMPLETED; + // This will ensure that we show 'Click to setup' UI if required. + state = LauncherAppWidgetInfo.FLAG_UI_NOT_READY; } else { state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY; } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 298b2c4691..19ddeec4a1 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -3889,7 +3889,8 @@ public class Launcher extends Activity if (!mIsSafeModeEnabled && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) - && ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) { + && (item.restoreStatus != LauncherAppWidgetInfo.RESTORE_COMPLETED)) { + if (appWidgetInfo == null) { if (DEBUG_WIDGETS) { Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId @@ -3899,42 +3900,51 @@ public class Launcher extends Activity LauncherModel.deleteItemFromDatabase(this, item); return; } - // Note: This assumes that the id remap broadcast is received before this step. - // If that is not the case, the id remap will be ignored and user may see the - // click to setup view. - PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null); - pendingInfo.spanX = item.spanX; - pendingInfo.spanY = item.spanY; - pendingInfo.minSpanX = item.minSpanX; - pendingInfo.minSpanY = item.minSpanY; - Bundle options = null; - WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo); - int newWidgetId = mAppWidgetHost.allocateAppWidgetId(); - boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( - newWidgetId, appWidgetInfo, options); + // If we do not have a valid id, try to bind an id. + if ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0) { + // Note: This assumes that the id remap broadcast is received before this step. + // If that is not the case, the id remap will be ignored and user may see the + // click to setup view. + PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(this, appWidgetInfo, null); + pendingInfo.spanX = item.spanX; + pendingInfo.spanY = item.spanY; + pendingInfo.minSpanX = item.minSpanX; + pendingInfo.minSpanY = item.minSpanY; + Bundle options = null; + WidgetHostViewLoader.getDefaultOptionsForWidget(this, pendingInfo); - // TODO consider showing a permission dialog when the widget is clicked. - if (!success) { - mAppWidgetHost.deleteAppWidgetId(newWidgetId); - if (DEBUG_WIDGETS) { - Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId - + " belongs to component " + item.providerName - + ", as the launcher is unable to bing a new widget id"); + int newWidgetId = mAppWidgetHost.allocateAppWidgetId(); + boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed( + newWidgetId, appWidgetInfo, options); + + // TODO consider showing a permission dialog when the widget is clicked. + if (!success) { + mAppWidgetHost.deleteAppWidgetId(newWidgetId); + if (DEBUG_WIDGETS) { + Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId + + " belongs to component " + item.providerName + + ", as the launcher is unable to bing a new widget id"); + } + LauncherModel.deleteItemFromDatabase(this, item); + return; } - LauncherModel.deleteItemFromDatabase(this, item); - return; + + item.appWidgetId = newWidgetId; + + // If the widget has a configure activity, it is still needs to set it up, otherwise + // the widget is ready to go. + item.restoreStatus = (appWidgetInfo.configure == null) + ? LauncherAppWidgetInfo.RESTORE_COMPLETED + : LauncherAppWidgetInfo.FLAG_UI_NOT_READY; + + LauncherModel.updateItemInDatabase(this, item); + } else if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0) + && (appWidgetInfo.configure == null)) { + // If the ID is already valid, verify if we need to configure or not. + item.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED; + LauncherModel.updateItemInDatabase(this, item); } - - item.appWidgetId = newWidgetId; - - // If the widget has a configure activity, it is still needs to set it up, otherwise - // the widget is ready to go. - item.restoreStatus = (appWidgetInfo.configure == null) - ? LauncherAppWidgetInfo.RESTORE_COMPLETED - : LauncherAppWidgetInfo.FLAG_UI_NOT_READY; - - LauncherModel.updateItemInDatabase(this, item); } if (!mIsSafeModeEnabled && item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index d9b0cd490d..6dfb4d93e1 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2194,7 +2194,7 @@ public class LauncherModel extends BroadcastReceiver // Id would be valid only if the widget restore broadcast was received. if (isIdValid) { - status = LauncherAppWidgetInfo.RESTORE_COMPLETED; + status = LauncherAppWidgetInfo.FLAG_UI_NOT_READY; } else { status &= ~LauncherAppWidgetInfo .FLAG_PROVIDER_NOT_READY; @@ -3197,6 +3197,12 @@ public class LauncherModel extends BroadcastReceiver widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY & ~LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; + + // adding this flag ensures that launcher shows 'click to setup' + // if the widget has a config activity. In case there is no config + // activity, it will be marked as 'restored' during bind. + widgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_UI_NOT_READY; + widgets.add(widgetInfo); updateItemInDatabase(context, widgetInfo); } From 13da3efb7ad2100f8d1263fa8f13a21691b86501 Mon Sep 17 00:00:00 2001 From: Geoff Mendal Date: Mon, 17 Aug 2015 07:09:24 -0700 Subject: [PATCH 47/76] Import translations. DO NOT MERGE Change-Id: Iedffa6e0a67907f0d86dfaf9b9863c4580cc3cf5 Auto-generated-cl: translation import --- res/values-fr/strings.xml | 2 +- res/values-ka-rGE/strings.xml | 3 +-- res/values-zh-rCN/strings.xml | 3 +-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 856690da8c..84e1594b4b 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -32,7 +32,7 @@ "Rechercher dans les applications" "Chargement des applications en cours…" "Aucune application ne correspond à la requête \"%1$s\"." - "Accéder au service %1$s" + "Accéder à %1$s" "Pas d\'espace libre sur cet écran d\'accueil." "Plus d\'espace disponible dans la zone de favoris." "Applications" diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml index 46a2343556..290033088e 100644 --- a/res/values-ka-rGE/strings.xml +++ b/res/values-ka-rGE/strings.xml @@ -32,8 +32,7 @@ "აპების ძიება" "აპები იტვირთება..." "„%1$s“-ის თანხვედრი აპები არ მოიძებნა" - - + "გადადი %1$s-ში" "ამ მთავარ ეკრანზე ადგილი აღარ არის." "რჩეულების თაროზე ადგილი არ არის" "აპები" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 495a14b37a..77f07c9559 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -32,8 +32,7 @@ "搜索应用" "正在加载应用…" "未找到与“%1$s”相符的应用" - - + "转到 %1$s" "此主屏幕上已没有空间。" "收藏栏已满" "应用" From ac36643be1fc6f7ef47090db73156bc4e57c68cc Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 14 Aug 2015 17:10:14 -0700 Subject: [PATCH 48/76] Updating the UI for the pending widget view Bug: 23183034 Change-Id: I2b3f6f765a51edf75e666186650bbd3cf7b66a7a --- res/values/dimens.xml | 4 + .../launcher3/PendingAppWidgetHostView.java | 202 +++++++++++------- 2 files changed, 126 insertions(+), 80 deletions(-) diff --git a/res/values/dimens.xml b/res/values/dimens.xml index 589d696666..b9fb6e2575 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -129,4 +129,8 @@ 4dp 2dp + + 8dp + 2dp + diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index 08f8e56018..e8c8b8ec99 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -16,13 +16,17 @@ package com.android.launcher3; +import android.annotation.TargetApi; import android.content.Context; import android.content.Intent; import android.content.res.Resources.Theme; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.text.Layout; import android.text.StaticLayout; @@ -32,6 +36,8 @@ import android.view.View; import android.view.View.OnClickListener; public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener { + private static final float SETUP_ICON_SIZE_FACTOR = 2f / 5; + private static final float MIN_SATUNATION = 0.7f; private static Theme sPreloaderTheme; @@ -47,13 +53,14 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen private Bitmap mIcon; private Drawable mCenterDrawable; - private Drawable mTopCornerDrawable; + private Drawable mSettingIconDrawable; private boolean mDrawableSizeChanged; private final TextPaint mPaint; private Layout mSetupTextLayout; + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info, boolean disabledForSafeMode) { super(context); @@ -70,6 +77,10 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen mLauncher.getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics())); setBackgroundResource(R.drawable.quantum_panel_dark); setWillNotDraw(false); + + if (Utilities.isLmpOrAbove()) { + setElevation(getResources().getDimension(R.dimen.pending_widget_elevation)); + } } @Override @@ -124,10 +135,12 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen FastBitmapDrawable disabledIcon = mLauncher.createIconDrawable(mIcon); disabledIcon.setGhostModeEnabled(true); mCenterDrawable = disabledIcon; - mTopCornerDrawable = null; + mSettingIconDrawable = null; } else if (isReadyForClickSetup()) { - mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting); - mTopCornerDrawable = new FastBitmapDrawable(mIcon); + mCenterDrawable = new FastBitmapDrawable(mIcon); + mSettingIconDrawable = getResources().getDrawable(R.drawable.ic_setting).mutate(); + + updateSettingColor(); } else { if (sPreloaderTheme == null) { sPreloaderTheme = getResources().newTheme(); @@ -137,13 +150,25 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen FastBitmapDrawable drawable = mLauncher.createIconDrawable(mIcon); mCenterDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme); mCenterDrawable.setCallback(this); - mTopCornerDrawable = null; + mSettingIconDrawable = null; applyState(); } mDrawableSizeChanged = true; } } + private void updateSettingColor() { + int color = Utilities.findDominantColorByHue(mIcon, 20); + // Make the dominant color bright. + float[] hsv = new float[3]; + Color.colorToHSV(color, hsv); + hsv[1] = Math.min(hsv[1], MIN_SATUNATION); + hsv[2] = 1; + color = Color.HSVToColor(hsv); + + mSettingIconDrawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); + } + @Override protected boolean verifyDrawable(Drawable who) { return (who == mCenterDrawable) || super.verifyDrawable(who); @@ -169,6 +194,83 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen && (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0; } + private void updateDrawableBounds() { + DeviceProfile grid = mLauncher.getDeviceProfile(); + int paddingTop = getPaddingTop(); + int paddingBottom = getPaddingBottom(); + int paddingLeft = getPaddingLeft(); + int paddingRight = getPaddingRight(); + + int minPadding = getResources() + .getDimensionPixelSize(R.dimen.pending_widget_min_padding); + + int availableWidth = getWidth() - paddingLeft - paddingRight - 2 * minPadding; + int availableHeight = getHeight() - paddingTop - paddingBottom - 2 * minPadding; + + if (mSettingIconDrawable == null) { + int outset = (mCenterDrawable instanceof PreloadIconDrawable) ? + ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0; + int maxSize = grid.iconSizePx + 2 * outset; + int size = Math.min(maxSize, Math.min(availableWidth, availableHeight)); + + mRect.set(0, 0, size, size); + mRect.inset(outset, outset); + mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); + mCenterDrawable.setBounds(mRect); + } else { + float iconSize = Math.min(availableWidth, availableHeight); + + // Use twice the setting size factor, as the setting is drawn at a corner and the + // icon is drawn in the center. + float settingIconScaleFactor = 1 + SETUP_ICON_SIZE_FACTOR * 2; + int maxSize = Math.max(availableWidth, availableHeight); + if (iconSize * settingIconScaleFactor > maxSize) { + // There is an overlap + iconSize = maxSize / settingIconScaleFactor; + } + + int actualIconSize = (int) Math.min(iconSize, grid.iconSizePx); + + // Recreate the setup text. + mSetupTextLayout = new StaticLayout( + getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth, + Layout.Alignment.ALIGN_CENTER, 1, 0, true); + int textHeight = mSetupTextLayout.getHeight(); + + // Extra icon size due to the setting icon + float minHeightWithText = textHeight + actualIconSize * settingIconScaleFactor + + grid.iconDrawablePaddingPx; + + int iconTop; + if (minHeightWithText < availableHeight) { + // We can draw the text as well + iconTop = (getHeight() - textHeight - + grid.iconDrawablePaddingPx - actualIconSize) / 2; + + } else { + // The text will not fit. Only draw the icons. + iconTop = (getHeight() - actualIconSize) / 2; + mSetupTextLayout = null; + } + + mRect.set(0, 0, actualIconSize, actualIconSize); + mRect.offset((getWidth() - actualIconSize) / 2, iconTop); + mCenterDrawable.setBounds(mRect); + + mRect.left = paddingLeft + minPadding; + mRect.right = mRect.left + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize); + mRect.top = paddingTop + minPadding; + mRect.bottom = mRect.top + (int) (SETUP_ICON_SIZE_FACTOR * actualIconSize); + mSettingIconDrawable.setBounds(mRect); + + if (mSetupTextLayout != null) { + // Set up position for dragging the text + mRect.left = paddingLeft + minPadding; + mRect.top = mCenterDrawable.getBounds().bottom + grid.iconDrawablePaddingPx; + } + } + } + @Override protected void onDraw(Canvas canvas) { if (mCenterDrawable == null) { @@ -176,81 +278,21 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen return; } - DeviceProfile grid = mLauncher.getDeviceProfile(); - if (mTopCornerDrawable == null) { - if (mDrawableSizeChanged) { - int outset = (mCenterDrawable instanceof PreloadIconDrawable) ? - ((PreloadIconDrawable) mCenterDrawable).getOutset() : 0; - int maxSize = grid.iconSizePx + 2 * outset; - int size = Math.min(maxSize, Math.min( - getWidth() - getPaddingLeft() - getPaddingRight(), - getHeight() - getPaddingTop() - getPaddingBottom())); - - mRect.set(0, 0, size, size); - mRect.inset(outset, outset); - mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); - mCenterDrawable.setBounds(mRect); - mDrawableSizeChanged = false; - } - mCenterDrawable.draw(canvas); - } else { - // Draw the top corner icon and "Setup" text is possible - if (mDrawableSizeChanged) { - int iconSize = grid.iconSizePx; - int paddingTop = getPaddingTop(); - int paddingBottom = getPaddingBottom(); - int paddingLeft = getPaddingLeft(); - int paddingRight = getPaddingRight(); - - int availableWidth = getWidth() - paddingLeft - paddingRight; - int availableHeight = getHeight() - paddingTop - paddingBottom; - - // Recreate the setup text. - mSetupTextLayout = new StaticLayout( - getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth, - Layout.Alignment.ALIGN_CENTER, 1, 0, true); - if (mSetupTextLayout.getLineCount() == 1) { - // The text fits in a single line. No need to draw the setup icon. - int size = Math.min(iconSize, Math.min(availableWidth, - availableHeight - mSetupTextLayout.getHeight())); - mRect.set(0, 0, size, size); - mRect.offsetTo((getWidth() - mRect.width()) / 2, - (getHeight() - mRect.height() - mSetupTextLayout.getHeight() - - grid.iconDrawablePaddingPx) / 2); - - mTopCornerDrawable.setBounds(mRect); - - // Update left and top to indicate the position where the text will be drawn. - mRect.left = paddingLeft; - mRect.top = mRect.bottom + grid.iconDrawablePaddingPx; - } else { - // The text can't be drawn in a single line. Draw a setup icon instead. - mSetupTextLayout = null; - int size = Math.min(iconSize, Math.min( - getWidth() - paddingLeft - paddingRight, - getHeight() - paddingTop - paddingBottom)); - mRect.set(0, 0, size, size); - mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); - mCenterDrawable.setBounds(mRect); - - size = Math.min(size / 2, - Math.max(mRect.top - paddingTop, mRect.left - paddingLeft)); - mTopCornerDrawable.setBounds(paddingLeft, paddingTop, - paddingLeft + size, paddingTop + size); - } - mDrawableSizeChanged = false; - } - - if (mSetupTextLayout == null) { - mCenterDrawable.draw(canvas); - mTopCornerDrawable.draw(canvas); - } else { - canvas.save(); - canvas.translate(mRect.left, mRect.top); - mSetupTextLayout.draw(canvas); - canvas.restore(); - mTopCornerDrawable.draw(canvas); - } + if (mDrawableSizeChanged) { + updateDrawableBounds(); + mDrawableSizeChanged = false; } + + mCenterDrawable.draw(canvas); + if (mSettingIconDrawable != null) { + mSettingIconDrawable.draw(canvas); + } + if (mSetupTextLayout != null) { + canvas.save(); + canvas.translate(mRect.left, mRect.top); + mSetupTextLayout.draw(canvas); + canvas.restore(); + } + } } From 522142af51fed5d23492c00bc8a37ec0cca8fdcc Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 17 Aug 2015 13:04:40 -0700 Subject: [PATCH 49/76] Removing call to isButtonPressed as its only Lollipop and above Change-Id: I0d70befa25baf4864303833111652dbf1d19daa5 --- src/com/android/launcher3/StylusEventHelper.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/com/android/launcher3/StylusEventHelper.java b/src/com/android/launcher3/StylusEventHelper.java index da46e6a548..e03273a6ea 100644 --- a/src/com/android/launcher3/StylusEventHelper.java +++ b/src/com/android/launcher3/StylusEventHelper.java @@ -1,8 +1,5 @@ package com.android.launcher3; -import com.android.launcher3.Utilities; - -import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; @@ -77,8 +74,9 @@ public class StylusEventHelper { * @param event The event to check. * @return Whether a stylus button press occurred. */ - public static boolean isStylusButtonPressed(MotionEvent event) { + private static boolean isStylusButtonPressed(MotionEvent event) { return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS - && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY); + && ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY) + == MotionEvent.BUTTON_SECONDARY); } } \ No newline at end of file From 9fc953b94dbc6b99e6de08c9dcc80a0cb8e3e319 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Mon, 17 Aug 2015 12:24:25 -0700 Subject: [PATCH 50/76] Version code cleanup > Renaming Lmp to Lollipop > Lollipop_MR1 instead of directly using 22 > Using M APIs directly instead of reflection Change-Id: I10a307f46e3be15b3299f549a2fd7e0e215a6a1b --- .../launcher3/WallpaperCropActivity.java | 6 +-- .../launcher3/WallpaperPickerActivity.java | 6 +-- src/com/android/launcher3/BubbleTextView.java | 2 +- .../android/launcher3/ButtonDropTarget.java | 9 ++-- src/com/android/launcher3/DragView.java | 6 +-- src/com/android/launcher3/Folder.java | 4 +- src/com/android/launcher3/Launcher.java | 54 ++++++------------- .../android/launcher3/LauncherAppState.java | 2 +- .../launcher3/LauncherBackupAgentHelper.java | 2 +- src/com/android/launcher3/LauncherClings.java | 5 +- .../android/launcher3/LauncherProvider.java | 2 +- .../LauncherStateTransitionAnimation.java | 10 ++-- src/com/android/launcher3/PagedView.java | 2 +- .../launcher3/PendingAppWidgetHostView.java | 2 +- .../launcher3/UninstallDropTarget.java | 2 +- src/com/android/launcher3/Utilities.java | 41 +++++++------- src/com/android/launcher3/Workspace.java | 2 +- .../compat/AppWidgetManagerCompat.java | 2 +- .../compat/AppWidgetManagerCompatV16.java | 6 +-- .../launcher3/compat/LauncherAppsCompat.java | 2 +- .../compat/LauncherAppsCompatV16.java | 3 +- .../compat/PackageInstallerCompat.java | 2 +- .../launcher3/compat/UserHandleCompat.java | 10 ++-- .../launcher3/compat/UserManagerCompat.java | 4 +- .../util/ManagedProfileHeuristic.java | 4 +- .../util/UiThreadCircularReveal.java | 2 +- .../launcher3/util/WallpaperUtils.java | 4 +- .../widget/WidgetHostViewLoader.java | 3 +- .../launcher3/widget/WidgetsListAdapter.java | 2 +- 29 files changed, 87 insertions(+), 114 deletions(-) diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java index f2bb509440..f2459dd3ec 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperCropActivity.java @@ -223,14 +223,12 @@ public class WallpaperCropActivity extends BaseActivity implements Handler.Callb @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) protected boolean isActivityDestroyed() { - return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) - && isDestroyed(); + return Utilities.ATLEAST_JB_MR1 && isDestroyed(); } @Thunk void addReusableBitmap(TileSource src) { synchronized (mReusableBitmaps) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT - && src instanceof BitmapRegionTileSource) { + if (Utilities.ATLEAST_KITKAT && src instanceof BitmapRegionTileSource) { Bitmap preview = ((BitmapRegionTileSource) src).getBitmap(); if (preview != null && preview.isMutable()) { mReusableBitmaps.add(preview); diff --git a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java index 75d8745529..59858501a5 100644 --- a/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java +++ b/WallpaperPicker/src/com/android/launcher3/WallpaperPickerActivity.java @@ -977,10 +977,8 @@ public class WallpaperPickerActivity extends WallpaperCropActivity { if (partner == null || !partner.hideDefaultWallpaper()) { // Add an entry for the default wallpaper (stored in system resources) - WallpaperTileInfo defaultWallpaperInfo = - (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) - ? getPreKKDefaultWallpaperInfo() - : getDefaultWallpaper(); + WallpaperTileInfo defaultWallpaperInfo = Utilities.ATLEAST_KITKAT + ? getDefaultWallpaper() : getPreKKDefaultWallpaperInfo(); if (defaultWallpaperInfo != null) { bundled.add(0, defaultWallpaperInfo); } diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 1bcaab5194..5070878244 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -510,7 +510,7 @@ public class BubbleTextView extends TextView mIcon.setBounds(0, 0, iconSize, iconSize); } if (mLayoutHorizontal) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { setCompoundDrawablesRelative(mIcon, null, null, null); } else { setCompoundDrawables(mIcon, null, null, null); diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java index b7f89d02a8..2baa6d8eed 100644 --- a/src/com/android/launcher3/ButtonDropTarget.java +++ b/src/com/android/launcher3/ButtonDropTarget.java @@ -24,7 +24,6 @@ import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.TargetApi; import android.content.Context; import android.content.res.ColorStateList; -import android.content.res.Configuration; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.PointF; @@ -93,7 +92,7 @@ public abstract class ButtonDropTarget extends TextView // drawableLeft and drawableStart. mDrawable = getResources().getDrawable(resId); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { setCompoundDrawablesRelativeWithIntrinsicBounds(mDrawable, null, null, null); } else { setCompoundDrawablesWithIntrinsicBounds(mDrawable, null, null, null); @@ -114,7 +113,7 @@ public abstract class ButtonDropTarget extends TextView @Override public final void onDragEnter(DragObject d) { d.dragView.setColor(mHoverColor); - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { animateTextColor(mHoverColor); } else { if (mCurrentFilter == null) { @@ -131,8 +130,8 @@ public abstract class ButtonDropTarget extends TextView // Do nothing } - protected void resetHoverColor() { - if (Utilities.isLmpOrAbove()) { + protected void resetHoverColor() { + if (Utilities.ATLEAST_LOLLIPOP) { animateTextColor(mOriginalTextColor.getDefaultColor()); } else { mDrawable.setColorFilter(null); diff --git a/src/com/android/launcher3/DragView.java b/src/com/android/launcher3/DragView.java index dfa8202a73..2acfc61403 100644 --- a/src/com/android/launcher3/DragView.java +++ b/src/com/android/launcher3/DragView.java @@ -131,7 +131,7 @@ public class DragView extends View { measure(ms, ms); mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { setElevation(getResources().getDimension(R.dimen.drag_elevation)); } } @@ -252,14 +252,14 @@ public class DragView extends View { setColorScale(color, m2); m1.postConcat(m2); - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { animateFilterTo(m1.getArray()); } else { mPaint.setColorFilter(new ColorMatrixColorFilter(m1)); invalidate(); } } else { - if (!Utilities.isLmpOrAbove() || mCurrentFilter == null) { + if (!Utilities.ATLEAST_LOLLIPOP || mCurrentFilter == null) { mPaint.setColorFilter(null); invalidate(); } else { diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index 7b88a88652..c1aa35669b 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -453,7 +453,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList Animator openFolderAnim = null; final Runnable onCompleteRunnable; - if (!Utilities.isLmpOrAbove()) { + if (!Utilities.ATLEAST_LOLLIPOP) { positionAndSizeAsIcon(); centerAboutIcon(); @@ -568,7 +568,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList public void onAnimationEnd(Animator animation) { mFolderName.animate().setDuration(FOLDER_NAME_ANIMATION_DURATION) .translationX(0) - .setInterpolator(Utilities.isLmpOrAbove() ? + .setInterpolator(Utilities.ATLEAST_LOLLIPOP ? AnimationUtils.loadInterpolator(mLauncher, android.R.interpolator.fast_out_slow_in) : new LogDecelerateInterpolator(100, 0)); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 19ddeec4a1..5aeacaa577 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -116,8 +116,6 @@ import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; @@ -360,18 +358,6 @@ public class Launcher extends Activity } } - // TODO: remove this field and call method directly when Launcher3 can depend on M APIs - private static Method sClipRevealMethod = null; - static { - Class activityOptionsClass = ActivityOptions.class; - try { - sClipRevealMethod = activityOptionsClass.getDeclaredMethod("makeClipRevealAnimation", - View.class, int.class, int.class, int.class, int.class); - } catch (Exception e) { - // Earlier version - } - } - @Thunk Runnable mBuildLayersRunnable = new Runnable() { public void run() { if (mWorkspace != null) { @@ -667,7 +653,7 @@ public class Launcher extends Activity @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static int generateViewId() { - if (Build.VERSION.SDK_INT >= 17) { + if (Utilities.ATLEAST_JB_MR1) { return View.generateViewId(); } else { // View.generateViewId() is not available. The following fallback logic is a copy @@ -1653,18 +1639,18 @@ public class Launcher extends Activity } registerReceiver(mReceiver, filter); FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView()); - setupTransparentSystemBarsForLmp(); + setupTransparentSystemBarsForLollipop(); mAttached = true; mVisible = true; } /** - * Sets up transparent navigation and status bars in LMP. + * Sets up transparent navigation and status bars in Lollipop. * This method is a no-op for other platform versions. */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) - private void setupTransparentSystemBarsForLmp() { - if (Utilities.isLmpOrAbove()) { + private void setupTransparentSystemBarsForLollipop() { + if (Utilities.ATLEAST_LOLLIPOP) { Window window = getWindow(); window.getAttributes().systemUiVisibility |= (View.SYSTEM_UI_FLAG_LAYOUT_STABLE @@ -2883,8 +2869,7 @@ public class Launcher extends Activity Bundle optsBundle = null; if (useLaunchAnimation) { ActivityOptions opts = null; - if (sClipRevealMethod != null) { - // TODO: call method directly when Launcher3 can depend on M APIs + if (Utilities.ATLEAST_MARSHMALLOW) { int left = 0, top = 0; int width = v.getMeasuredWidth(), height = v.getMeasuredHeight(); if (v instanceof TextView) { @@ -2898,22 +2883,12 @@ public class Launcher extends Activity height = bounds.height(); } } - try { - opts = (ActivityOptions) sClipRevealMethod.invoke(null, v, - left, top, width, height); - } catch (IllegalAccessException e) { - Log.d(TAG, "Could not call makeClipRevealAnimation: " + e); - sClipRevealMethod = null; - } catch (InvocationTargetException e) { - Log.d(TAG, "Could not call makeClipRevealAnimation: " + e); - sClipRevealMethod = null; - } - } - if (opts == null && !Utilities.isLmpOrAbove()) { + opts = ActivityOptions.makeClipRevealAnimation(v, left, top, width, height); + } else if (!Utilities.ATLEAST_LOLLIPOP) { // Below L, we use a scale up animation opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight()); - } else if (opts == null && Utilities.isLmpMR1()) { + } else if (Utilities.ATLEAST_LOLLIPOP_MR1) { // On L devices, we use the device default slide-up transition. // On L MR1 devices, we a custom version of the slide-up transition which // doesn't have the delay present in the device default. @@ -3028,7 +3003,7 @@ public class Launcher extends Activity ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha, scaleX, scaleY); - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { oa.setInterpolator(new LogDecelerateInterpolator(100, 0)); } oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration)); @@ -4324,13 +4299,14 @@ public class Launcher extends Activity return oriMap[(d.getRotation() + indexOffset) % 4]; } + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public void lockScreenOrientation() { if (mRotationEnabled) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (Utilities.ATLEAST_JB_MR2) { + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); + } else { setRequestedOrientation(mapConfigurationOriActivityInfoOri(getResources() .getConfiguration().orientation)); - } else { - setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED); } } } @@ -4534,7 +4510,7 @@ public class Launcher extends Activity public ItemInfo createAppDragInfo(Intent appLaunchIntent) { // Called from search suggestion UserHandleCompat user = null; - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { UserHandle userHandle = appLaunchIntent.getParcelableExtra(Intent.EXTRA_USER); if (userHandle != null) { user = UserHandleCompat.fromUser(userHandle); diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 52f85ea428..d87ad67e5f 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -127,7 +127,7 @@ public class LauncherAppState { LauncherModel setLauncher(Launcher launcher) { getLauncherProvider().setLauncherProviderChangeListener(launcher); mModel.initialize(launcher); - mAccessibilityDelegate = ((launcher != null) && Utilities.isLmpOrAbove()) ? + mAccessibilityDelegate = ((launcher != null) && Utilities.ATLEAST_LOLLIPOP) ? new LauncherAccessibilityDelegate(launcher) : null; return mModel; } diff --git a/src/com/android/launcher3/LauncherBackupAgentHelper.java b/src/com/android/launcher3/LauncherBackupAgentHelper.java index 3debef60e0..8eb4e63694 100644 --- a/src/com/android/launcher3/LauncherBackupAgentHelper.java +++ b/src/com/android/launcher3/LauncherBackupAgentHelper.java @@ -65,7 +65,7 @@ public class LauncherBackupAgentHelper extends BackupAgentHelper { @Override public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException { - if (!Utilities.isLmpOrAbove()) { + if (!Utilities.ATLEAST_LOLLIPOP) { // No restore for old devices. Log.i(TAG, "You shall not pass!!!"); Log.d(TAG, "Restore is only supported on devices running Lollipop and above."); diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index c44ea6bcdc..7470284740 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -16,8 +16,6 @@ package com.android.launcher3; -import android.accounts.Account; -import android.accounts.AccountManager; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.annotation.TargetApi; @@ -36,6 +34,7 @@ import android.view.View.OnLongClickListener; import android.view.ViewGroup; import android.view.ViewTreeObserver.OnGlobalLayoutListener; import android.view.accessibility.AccessibilityManager; + import com.android.launcher3.util.Thunk; class LauncherClings implements OnClickListener { @@ -225,7 +224,7 @@ class LauncherClings implements OnClickListener { // Restricted secondary users (child mode) will potentially have very few apps // seeded when they start up for the first time. Clings won't work well with that - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (Utilities.ATLEAST_JB_MR2) { UserManager um = (UserManager) mLauncher.getSystemService(Context.USER_SERVICE); Bundle restrictions = um.getUserRestrictions(); if (restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false)) { diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 0599714057..20844ec132 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -402,7 +402,7 @@ public class LauncherProvider extends ContentProvider { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction() { // UserManager.getApplicationRestrictions() requires minSdkVersion >= 18 - if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (!Utilities.ATLEAST_JB_MR2) { return null; } diff --git a/src/com/android/launcher3/LauncherStateTransitionAnimation.java b/src/com/android/launcher3/LauncherStateTransitionAnimation.java index 5a3f3dc7ac..cdde8c13fa 100644 --- a/src/com/android/launcher3/LauncherStateTransitionAnimation.java +++ b/src/com/android/launcher3/LauncherStateTransitionAnimation.java @@ -205,7 +205,7 @@ public class LauncherStateTransitionAnimation { final boolean animated, final PrivateTransitionCallbacks pCb) { final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final Resources res = mLauncher.getResources(); - final boolean material = Utilities.isLmpOrAbove(); + final boolean material = Utilities.ATLEAST_LOLLIPOP; final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger); @@ -358,12 +358,11 @@ public class LauncherStateTransitionAnimation { dispatchOnLauncherTransitionStart(toView, animated, false); // Enable all necessary layers - boolean isLmpOrAbove = Utilities.isLmpOrAbove(); for (View v : layerViews.keySet()) { if (layerViews.get(v) == BUILD_AND_SET_LAYER) { v.setLayerType(View.LAYER_TYPE_HARDWARE, null); } - if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) { + if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) { v.buildLayer(); } } @@ -486,7 +485,7 @@ public class LauncherStateTransitionAnimation { final PrivateTransitionCallbacks pCb) { final AnimatorSet animation = LauncherAnimUtils.createAnimatorSet(); final Resources res = mLauncher.getResources(); - final boolean material = Utilities.isLmpOrAbove(); + final boolean material = Utilities.ATLEAST_LOLLIPOP; final int revealDuration = res.getInteger(R.integer.config_overlayRevealTime); final int itemsAlphaStagger = res.getInteger(R.integer.config_overlayItemsAlphaStagger); @@ -668,12 +667,11 @@ public class LauncherStateTransitionAnimation { dispatchOnLauncherTransitionStart(toView, animated, false); // Enable all necessary layers - boolean isLmpOrAbove = Utilities.isLmpOrAbove(); for (View v : layerViews.keySet()) { if (layerViews.get(v) == BUILD_AND_SET_LAYER) { v.setLayerType(View.LAYER_TYPE_HARDWARE, null); } - if (isLmpOrAbove && Utilities.isViewAttachedToWindow(v)) { + if (Utilities.ATLEAST_LOLLIPOP && Utilities.isViewAttachedToWindow(v)) { v.buildLayer(); } } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index 218c1a36f3..05f0a05534 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -2293,7 +2293,7 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc // Besides disabling the accessibility long-click, this also prevents this view from getting // accessibility focus. info.setLongClickable(false); - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { info.removeAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK); } } diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index e8c8b8ec99..40eadabd5c 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -78,7 +78,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen setBackgroundResource(R.drawable.quantum_panel_dark); setWillNotDraw(false); - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { setElevation(getResources().getDimension(R.dimen.pending_widget_elevation)); } } diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java index 0819f8ce01..955d4013cc 100644 --- a/src/com/android/launcher3/UninstallDropTarget.java +++ b/src/com/android/launcher3/UninstallDropTarget.java @@ -38,7 +38,7 @@ public class UninstallDropTarget extends ButtonDropTarget { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) public static boolean supportsDrop(Context context, Object info) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { + if (Utilities.ATLEAST_JB_MR2) { UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE); Bundle restrictions = userManager.getUserRestrictions(); if (restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false) diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 8fd298df72..adedd33b2f 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -87,6 +87,24 @@ public final class Utilities { private static final int[] sLoc0 = new int[2]; private static final int[] sLoc1 = new int[2]; + // TODO: use Build.VERSION_CODES when available + public static final boolean ATLEAST_MARSHMALLOW = Build.VERSION.SDK_INT >= 23; + + public static final boolean ATLEAST_LOLLIPOP_MR1 = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1; + + public static final boolean ATLEAST_LOLLIPOP = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; + + public static final boolean ATLEAST_KITKAT = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; + + public static final boolean ATLEAST_JB_MR1 = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1; + + public static final boolean ATLEAST_JB_MR2 = + Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2; + // To turn on these properties, type // adb shell setprop log.tag.PROPERTY_NAME [VERBOSE | SUPPRESS] private static final String FORCE_ENABLE_ROTATION_PROPERTY = "launcher_force_rotate"; @@ -110,23 +128,6 @@ public final class Utilities { return sForceEnableRotation || context.getResources().getBoolean(R.bool.allow_rotation); } - /** - * Indicates if the device is running LMP or higher. - */ - public static boolean isLmpOrAbove() { - return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP; - } - - public static boolean isLmpMR1OrAbove() { - // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22; - return Build.VERSION.SDK_INT >= 22; - } - - public static boolean isLmpMR1() { - // TODO(adamcohen): update to Build.VERSION_CODES.LOLLIPOP_MR1 once building against 22; - return Build.VERSION.SDK_INT == 22; - } - public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { byte[] data = c.getBlob(iconIndex); try { @@ -517,7 +518,7 @@ public final class Utilities { @TargetApi(Build.VERSION_CODES.KITKAT) public static boolean isViewAttachedToWindow(View v) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { + if (ATLEAST_KITKAT) { return v.isAttachedToWindow(); } else { // A proxy call which returns null, if the view is not attached to the window. @@ -544,7 +545,7 @@ public final class Utilities { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) { if (info.provider.getPackageName().equals(providerPkg)) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (ATLEAST_JB_MR1) { if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) { return info; } else if (defaultWidgetForSearchPackage == null) { @@ -655,7 +656,7 @@ public final class Utilities { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static boolean isRtl(Resources res) { - return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) && + return ATLEAST_JB_MR1 && (res.getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL); } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 08e642948b..61d09a95c4 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2013,7 +2013,7 @@ public class Workspace extends PagedView } public void updateAccessibilityFlags() { - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { int total = getPageCount(); for (int i = numCustomPages(); i < total; i++) { updateAccessibilityFlags((CellLayout) getPageAt(i), i); diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java index 7aa36d447b..434f13dcc1 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompat.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompat.java @@ -40,7 +40,7 @@ public abstract class AppWidgetManagerCompat { public static AppWidgetManagerCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { sInstance = new AppWidgetManagerCompatVL(context.getApplicationContext()); } else { sInstance = new AppWidgetManagerCompatV16(context.getApplicationContext()); diff --git a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java index f7f4b7e4f2..463cf902d1 100644 --- a/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java +++ b/src/com/android/launcher3/compat/AppWidgetManagerCompatV16.java @@ -54,10 +54,10 @@ class AppWidgetManagerCompatV16 extends AppWidgetManagerCompat { @Override public boolean bindAppWidgetIdIfAllowed(int appWidgetId, AppWidgetProviderInfo info, Bundle options) { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { - return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider); - } else { + if (Utilities.ATLEAST_JB_MR1) { return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider, options); + } else { + return mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId, info.provider); } } diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java index 5858bc8b9e..95e3ba9024 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompat.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java @@ -53,7 +53,7 @@ public abstract class LauncherAppsCompat { public static LauncherAppsCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { sInstance = new LauncherAppsCompatVL(context.getApplicationContext()); } else { sInstance = new LauncherAppsCompatV16(context.getApplicationContext()); diff --git a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java index ac3d252f57..339c457e18 100644 --- a/src/com/android/launcher3/compat/LauncherAppsCompatV16.java +++ b/src/com/android/launcher3/compat/LauncherAppsCompatV16.java @@ -31,6 +31,7 @@ import android.os.Build; import android.os.Bundle; import android.provider.Settings; +import com.android.launcher3.Utilities; import com.android.launcher3.util.Thunk; import java.util.ArrayList; @@ -188,7 +189,7 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat { // when moving a package or mounting/un-mounting external storage. Assume that // it is a replacing operation. final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, - Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT); + !Utilities.ATLEAST_KITKAT); String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST); for (OnAppsChangedCallbackCompat callback : getCallbacks()) { callback.onPackagesAvailable(packages, user, replacing); diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java index c499083283..ec5014d7c3 100644 --- a/src/com/android/launcher3/compat/PackageInstallerCompat.java +++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java @@ -34,7 +34,7 @@ public abstract class PackageInstallerCompat { public static PackageInstallerCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { sInstance = new PackageInstallerCompatVL(context); } else { sInstance = new PackageInstallerCompatV16(); diff --git a/src/com/android/launcher3/compat/UserHandleCompat.java b/src/com/android/launcher3/compat/UserHandleCompat.java index ab4b7216b3..567022b437 100644 --- a/src/com/android/launcher3/compat/UserHandleCompat.java +++ b/src/com/android/launcher3/compat/UserHandleCompat.java @@ -34,7 +34,7 @@ public class UserHandleCompat { @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) public static UserHandleCompat myUserHandle() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { return new UserHandleCompat(android.os.Process.myUserHandle()); } else { return new UserHandleCompat(); @@ -55,7 +55,7 @@ public class UserHandleCompat { @Override public String toString() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { return mUser.toString(); } else { return ""; @@ -67,7 +67,7 @@ public class UserHandleCompat { if (!(other instanceof UserHandleCompat)) { return false; } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { return mUser.equals(((UserHandleCompat) other).mUser); } else { return true; @@ -76,7 +76,7 @@ public class UserHandleCompat { @Override public int hashCode() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { return mUser.hashCode(); } else { return 0; @@ -89,7 +89,7 @@ public class UserHandleCompat { * profiles so this is a no-op. */ public void addToIntent(Intent intent, String name) { - if (Utilities.isLmpOrAbove() && mUser != null) { + if (Utilities.ATLEAST_LOLLIPOP && mUser != null) { intent.putExtra(name, mUser); } } diff --git a/src/com/android/launcher3/compat/UserManagerCompat.java b/src/com/android/launcher3/compat/UserManagerCompat.java index 2ff1e7b749..f708004a33 100644 --- a/src/com/android/launcher3/compat/UserManagerCompat.java +++ b/src/com/android/launcher3/compat/UserManagerCompat.java @@ -34,9 +34,9 @@ public abstract class UserManagerCompat { public static UserManagerCompat getInstance(Context context) { synchronized (sInstanceLock) { if (sInstance == null) { - if (Utilities.isLmpOrAbove()) { + if (Utilities.ATLEAST_LOLLIPOP) { sInstance = new UserManagerCompatVL(context.getApplicationContext()); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + } else if (Utilities.ATLEAST_JB_MR1) { sInstance = new UserManagerCompatV17(context.getApplicationContext()); } else { sInstance = new UserManagerCompatV16(); diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java index b37f44719d..74fc92a04e 100644 --- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java +++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java @@ -68,7 +68,7 @@ public class ManagedProfileHeuristic { private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000; public static ManagedProfileHeuristic get(Context context, UserHandleCompat user) { - if (Utilities.isLmpOrAbove() && !UserHandleCompat.myUserHandle().equals(user)) { + if (Utilities.ATLEAST_LOLLIPOP && !UserHandleCompat.myUserHandle().equals(user)) { return new ManagedProfileHeuristic(context, user); } return null; @@ -296,7 +296,7 @@ public class ManagedProfileHeuristic { * Verifies that entries corresponding to {@param users} exist and removes all invalid entries. */ public static void processAllUsers(List users, Context context) { - if (!Utilities.isLmpOrAbove()) { + if (!Utilities.ATLEAST_LOLLIPOP) { return; } UserManagerCompat userManager = UserManagerCompat.getInstance(context); diff --git a/src/com/android/launcher3/util/UiThreadCircularReveal.java b/src/com/android/launcher3/util/UiThreadCircularReveal.java index 3ca3aeeeec..f2b5e5e154 100644 --- a/src/com/android/launcher3/util/UiThreadCircularReveal.java +++ b/src/com/android/launcher3/util/UiThreadCircularReveal.java @@ -47,7 +47,7 @@ public class UiThreadCircularReveal { float progress = arg0.getAnimatedFraction(); outlineProvider.setProgress(progress); revealView.invalidateOutline(); - if (!Utilities.isLmpMR1OrAbove()) { + if (!Utilities.ATLEAST_LOLLIPOP_MR1) { revealView.invalidate(); } } diff --git a/src/com/android/launcher3/util/WallpaperUtils.java b/src/com/android/launcher3/util/WallpaperUtils.java index 53b2acd840..b9fccbcfd7 100644 --- a/src/com/android/launcher3/util/WallpaperUtils.java +++ b/src/com/android/launcher3/util/WallpaperUtils.java @@ -24,6 +24,8 @@ import android.graphics.Point; import android.os.Build; import android.view.WindowManager; +import com.android.launcher3.Utilities; + /** * Utility methods for wallpaper management. */ @@ -99,7 +101,7 @@ public final class WallpaperUtils { int maxDim = Math.max(maxDims.x, maxDims.y); int minDim = Math.max(minDims.x, minDims.y); - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { Point realSize = new Point(); windowManager.getDefaultDisplay().getRealSize(realSize); maxDim = Math.max(realSize.x, realSize.y); diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index 30b3d581a9..461aebb6b8 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -15,6 +15,7 @@ import com.android.launcher3.DragLayer; import com.android.launcher3.DragSource; import com.android.launcher3.Launcher; import com.android.launcher3.LauncherAppWidgetProviderInfo; +import com.android.launcher3.Utilities; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.util.Thunk; @@ -131,7 +132,7 @@ public class WidgetHostViewLoader implements DragListener { public static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) { Bundle options = null; Rect rect = new Rect(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, rect); Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher, info.componentName, null); diff --git a/src/com/android/launcher3/widget/WidgetsListAdapter.java b/src/com/android/launcher3/widget/WidgetsListAdapter.java index a54626a391..f1cde299d0 100644 --- a/src/com/android/launcher3/widget/WidgetsListAdapter.java +++ b/src/com/android/launcher3/widget/WidgetsListAdapter.java @@ -169,7 +169,7 @@ public class WidgetsListAdapter extends Adapter { // if the end padding is 0, then container view (horizontal scroll view) doesn't respect // the end of the linear layout width + the start padding and doesn't allow scrolling. - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (Utilities.ATLEAST_JB_MR1) { cellList.setPaddingRelative(mIndent, 0, 1, 0); } else { cellList.setPadding(mIndent, 0, 1, 0); From 7c3cccf427be23990d7bbc797a3106de69389586 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Tue, 18 Aug 2015 18:48:38 -0700 Subject: [PATCH 51/76] Fixing empty screen created at the end, even when dragging the last item on the last screen. Change-Id: If0d4faf0ca15e71facb6ed2fd81ed6a589527836 --- src/com/android/launcher3/Workspace.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 61d09a95c4..44d70ba790 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2319,14 +2319,14 @@ public class Workspace extends PagedView throw new IllegalStateException(msg); } - DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), - DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible); - dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); - if (child.getParent() instanceof ShortcutAndWidgetContainer) { mDragSourceInternal = (ShortcutAndWidgetContainer) child.getParent(); } + DragView dv = mDragController.startDrag(b, dragLayerX, dragLayerY, source, child.getTag(), + DragController.DRAG_ACTION_MOVE, dragVisualizeOffset, dragRect, scale, accessible); + dv.setIntrinsicIconScaleFactor(source.getIntrinsicIconScaleFactor()); + b.recycle(); } From 15f8b17adb36e7345e39cc1c431f8c1abd84f832 Mon Sep 17 00:00:00 2001 From: Winson Date: Wed, 19 Aug 2015 11:02:39 -0700 Subject: [PATCH 52/76] Deferring the move to the default screen until after we callback to the callbacks. - Since move to default screen starts the scroller immediately, any delays in handling onNewIntent and onHomeIntent in the callbacks will cause the scroller to skip frames the next time it updates. This change will defer updating the page to the default screen until after onNewIntent is called back (and all its posted runnables). Bug: 22929080 Change-Id: Ibab6106938721702d4da23faaca99039861e10dc --- src/com/android/launcher3/Launcher.java | 44 +++++++++++++++---------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 298b2c4691..203e2247c5 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -1867,29 +1867,22 @@ public class Launcher extends Activity super.onNewIntent(intent); // Close the menu - if (Intent.ACTION_MAIN.equals(intent.getAction())) { + Folder openFolder = mWorkspace.getOpenFolder(); + boolean alreadyOnHome = mHasFocus && ((intent.getFlags() & + Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) + != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); + boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction()); + if (isActionMain) { // also will cancel mWaitingForResult. closeSystemDialogs(); - final boolean alreadyOnHome = mHasFocus && ((intent.getFlags() & - Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) - != Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT); - if (mWorkspace == null) { // Can be cases where mWorkspace is null, this prevents a NPE return; } - Folder openFolder = mWorkspace.getOpenFolder(); // In all these cases, only animate if we're already on home mWorkspace.exitWidgetResizeMode(); - boolean moveToDefaultScreen = mLauncherCallbacks != null ? - mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true; - if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() && - openFolder == null && moveToDefaultScreen) { - mWorkspace.moveToDefaultScreen(true); - } - closeFolder(); exitSpringLoadedDragMode(); @@ -1923,13 +1916,30 @@ public class Launcher extends Activity } } - if (DEBUG_RESUME_TIME) { - Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime)); - } - if (mLauncherCallbacks != null) { mLauncherCallbacks.onNewIntent(intent); } + + // Defer moving to the default screen until after we callback to the LauncherCallbacks + // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage + // animation. + if (isActionMain) { + boolean moveToDefaultScreen = mLauncherCallbacks != null ? + mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true; + if (alreadyOnHome && mState == State.WORKSPACE && !mWorkspace.isTouchActive() && + openFolder == null && moveToDefaultScreen) { + mWorkspace.post(new Runnable() { + @Override + public void run() { + mWorkspace.moveToDefaultScreen(true); + } + }); + } + } + + if (DEBUG_RESUME_TIME) { + Log.d(TAG, "Time spent in onNewIntent: " + (System.currentTimeMillis() - startTime)); + } } @Override From 639e906a0fc205b48512951feec0cbb47513e246 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 19 Aug 2015 19:17:06 -0700 Subject: [PATCH 53/76] Fixing work folder addition logic > Showing animation when folder is added > Not showing animation while cling is visible > Checking is loader completed from UI thread > Running bind complete runnables when bind is completed Bug: 21772992 Bug: 23181585 Change-Id: I50742850da8294a877478ce02b107a026f68b563 --- src/com/android/launcher3/Launcher.java | 37 +++++---- src/com/android/launcher3/LauncherClings.java | 8 ++ src/com/android/launcher3/LauncherModel.java | 78 +++++++++---------- 3 files changed, 64 insertions(+), 59 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 6f2458c113..a59e9ae0cd 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -301,6 +301,8 @@ public class Launcher extends Activity private boolean mHasFocus = false; private boolean mAttached = false; + private LauncherClings mClings; + private static LongArrayMap sFolders = new LongArrayMap<>(); private View.OnTouchListener mHapticFeedbackTouchListener; @@ -648,7 +650,7 @@ public class Launcher extends Activity public boolean isDraggingEnabled() { // We prevent dragging when we are loading the workspace as it is possible to pick up a view // that is subsequently removed from the workspace in startBinding(). - return !mModel.isLoadingWorkspace(); + return !isWorkspaceLoading(); } @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @@ -3755,11 +3757,12 @@ public class Launcher extends Activity continue; } + final View view; switch (item.itemType) { case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: ShortcutInfo info = (ShortcutInfo) item; - View shortcut = createShortcut(info); + view = createShortcut(info); /* * TODO: FIX collision case @@ -3778,28 +3781,26 @@ public class Launcher extends Activity } } } - - workspace.addInScreenFromBind(shortcut, item.container, item.screenId, item.cellX, - item.cellY, 1, 1); - if (animateIcons) { - // Animate all the applications up now - shortcut.setAlpha(0f); - shortcut.setScaleX(0f); - shortcut.setScaleY(0f); - bounceAnims.add(createNewAppBounceAnimation(shortcut, i)); - newShortcutsScreenId = item.screenId; - } break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - FolderIcon newFolder = FolderIcon.fromXml(R.layout.folder_icon, this, + view = FolderIcon.fromXml(R.layout.folder_icon, this, (ViewGroup) workspace.getChildAt(workspace.getCurrentPage()), (FolderInfo) item, mIconCache); - workspace.addInScreenFromBind(newFolder, item.container, item.screenId, item.cellX, - item.cellY, 1, 1); break; default: throw new RuntimeException("Invalid Item Type"); } + + workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX, + item.cellY, 1, 1); + if (animateIcons) { + // Animate all the applications up now + view.setAlpha(0f); + view.setScaleX(0f); + view.setScaleY(0f); + bounceAnims.add(createNewAppBounceAnimation(view, i)); + newShortcutsScreenId = item.screenId; + } } if (animateIcons) { @@ -4064,7 +4065,8 @@ public class Launcher extends Activity private boolean canRunNewAppsAnimation() { long diff = System.currentTimeMillis() - mDragController.getLastGestureUpTime(); - return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000); + return diff > (NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS * 1000) + && (mClings == null || !mClings.isVisible()); } private ValueAnimator createNewAppBounceAnimation(View v, int i) { @@ -4491,6 +4493,7 @@ public class Launcher extends Activity // launcher2). Otherwise, we prompt the user upon started for migration LauncherClings launcherClings = new LauncherClings(this); if (launcherClings.shouldShowFirstRunOrMigrationClings()) { + mClings = launcherClings; if (mModel.canMigrateFromOldLauncherDb(this)) { launcherClings.showMigrationCling(); } else { diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java index 7470284740..18fe8ef868 100644 --- a/src/com/android/launcher3/LauncherClings.java +++ b/src/com/android/launcher3/LauncherClings.java @@ -51,6 +51,7 @@ class LauncherClings implements OnClickListener { @Thunk Launcher mLauncher; private LayoutInflater mInflater; + @Thunk boolean mIsVisible; /** Ctor */ public LauncherClings(Launcher launcher) { @@ -91,6 +92,7 @@ class LauncherClings implements OnClickListener { * package was not preinstalled and there exists a db to migrate from. */ public void showMigrationCling() { + mIsVisible = true; mLauncher.hideWorkspaceSearchAndHotseat(); ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher); @@ -117,6 +119,7 @@ class LauncherClings implements OnClickListener { } public void showLongPressCling(boolean showWelcome) { + mIsVisible = true; ViewGroup root = (ViewGroup) mLauncher.findViewById(R.id.launcher); View cling = mInflater.inflate(R.layout.longpress_cling, root, false); @@ -196,6 +199,7 @@ class LauncherClings implements OnClickListener { mLauncher.getSharedPrefs().edit() .putBoolean(flag, true) .apply(); + mIsVisible = false; if (postAnimationCb != null) { postAnimationCb.run(); } @@ -209,6 +213,10 @@ class LauncherClings implements OnClickListener { } } + public boolean isVisible() { + return mIsVisible; + } + /** Returns whether the clings are enabled or should be shown */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2) private boolean areClingsEnabled() { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 27486822a7..e5ca77867d 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -34,7 +34,6 @@ import android.content.pm.ResolveInfo; import android.database.Cursor; import android.graphics.Bitmap; import android.net.Uri; -import android.os.Build; import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; @@ -259,7 +258,7 @@ public class LauncherModel extends BroadcastReceiver /** Runs the specified runnable immediately if called from the worker thread, otherwise it is * posted on the worker thread handler. */ - private static void runOnWorkerThread(Runnable r) { + @Thunk static void runOnWorkerThread(Runnable r) { if (sWorkerThread.getThreadId() == Process.myTid()) { r.run(); } else { @@ -268,19 +267,6 @@ public class LauncherModel extends BroadcastReceiver } } - /** - * Runs the specified runnable after the loader is complete - */ - @Thunk void runAfterBindCompletes(Runnable r) { - if (isLoadingWorkspace() || !mHasLoaderCompletedOnce) { - synchronized (mBindCompleteRunnables) { - mBindCompleteRunnables.add(r); - } - } else { - runOnWorkerThread(r); - } - } - boolean canMigrateFromOldLauncherDb(Launcher launcher) { return mOldContentProviderExists && !launcher.isLauncherPreinstalled() ; } @@ -894,8 +880,13 @@ public class LauncherModel extends BroadcastReceiver } private void assertWorkspaceLoaded() { - if (LauncherAppState.isDogfoodBuild() && (isLoadingWorkspace() || !mHasLoaderCompletedOnce)) { - throw new RuntimeException("Trying to add shortcut while loader is running"); + if (LauncherAppState.isDogfoodBuild()) { + synchronized (mLock) { + if (!mHasLoaderCompletedOnce || + (mLoaderTask != null && mLoaderTask.mIsLoadingAndBindingWorkspace)) { + throw new RuntimeException("Trying to add shortcut while loader is running"); + } + } } } @@ -1390,16 +1381,6 @@ public class LauncherModel extends BroadcastReceiver mHandler.post(r); } } - - // Run all the bind complete runnables after workspace is bound. - if (!mBindCompleteRunnables.isEmpty()) { - synchronized (mBindCompleteRunnables) { - for (final Runnable r : mBindCompleteRunnables) { - runOnWorkerThread(r); - } - mBindCompleteRunnables.clear(); - } - } } public void stopLoader() { @@ -1441,15 +1422,6 @@ public class LauncherModel extends BroadcastReceiver return mAllAppsLoaded; } - boolean isLoadingWorkspace() { - synchronized (mLock) { - if (mLoaderTask != null) { - return mLoaderTask.isLoadingWorkspace(); - } - } - return false; - } - /** * Runnable for the thread that loads the contents of the launcher: * - workspace icons @@ -1468,10 +1440,6 @@ public class LauncherModel extends BroadcastReceiver mFlags = flags; } - boolean isLoadingWorkspace() { - return mIsLoadingAndBindingWorkspace; - } - private void loadAndBindWorkspace() { mIsLoadingAndBindingWorkspace = true; @@ -2697,13 +2665,24 @@ public class LauncherModel extends BroadcastReceiver callbacks.finishBindingItems(); } + mIsLoadingAndBindingWorkspace = false; + + // Run all the bind complete runnables after workspace is bound. + if (!mBindCompleteRunnables.isEmpty()) { + synchronized (mBindCompleteRunnables) { + for (final Runnable r : mBindCompleteRunnables) { + runOnWorkerThread(r); + } + mBindCompleteRunnables.clear(); + } + } + // If we're profiling, ensure this is the last thing in the queue. if (DEBUG_LOADERS) { Log.d(TAG, "bound workspace in " + (SystemClock.uptimeMillis()-t) + "ms"); } - mIsLoadingAndBindingWorkspace = false; } }; if (isLoadingSynchronously) { @@ -2832,12 +2811,27 @@ public class LauncherModel extends BroadcastReceiver final ManagedProfileHeuristic heuristic = ManagedProfileHeuristic.get(mContext, user); if (heuristic != null) { - runAfterBindCompletes(new Runnable() { + final Runnable r = new Runnable() { @Override public void run() { heuristic.processUserApps(apps); } + }; + runOnMainThread(new Runnable() { + + @Override + public void run() { + // Check isLoadingWorkspace on the UI thread, as it is updated on + // the UI thread. + if (mIsLoadingAndBindingWorkspace) { + synchronized (mBindCompleteRunnables) { + mBindCompleteRunnables.add(r); + } + } else { + runOnWorkerThread(r); + } + } }); } } From d2eb49e4c3bb37d35e72c36d8a308262b690075f Mon Sep 17 00:00:00 2001 From: Winson Date: Tue, 18 Aug 2015 17:43:02 -0700 Subject: [PATCH 54/76] Tweaking fast scroller to follow touch closer. - Issue: The fast scroller currently does not follow the touch input because fundamentally, the fixed scrollbar height and the mapping of the scroll space to the scrollbar space is fundamentally incompatible. - This CL changes the fast scroller to allow it to detach when the user fast-scrolls, then re-attaches after the user scrolls the screen and the current scroll position for the scrollbar picks up the thumb position. - Since the scroll position and the fast scroller thumb is now detached, we can change the distribution of the fast scroll letters to make it independent of the rows for each section and instead uniformly distribute it along the scrollbar, which allows for more stability. - There are edge cases where this fails, especially when there are few apps, which we can investigate further. Bug: 20035978 Change-Id: I8322f862107e6f330deff692885233706564bffd --- proguard.flags | 4 +- .../android/launcher3/BaseRecyclerView.java | 40 ++++++----- .../BaseRecyclerViewFastScrollBar.java | 38 +++++++++-- .../allapps/AllAppsContainerView.java | 3 +- .../allapps/AllAppsRecyclerView.java | 68 ++++++++++++++++--- .../allapps/AlphabeticalAppsList.java | 57 ++++++++++------ .../launcher3/widget/WidgetsRecyclerView.java | 12 ++-- 7 files changed, 161 insertions(+), 61 deletions(-) diff --git a/proguard.flags b/proguard.flags index a8e2b60929..b4365fee40 100644 --- a/proguard.flags +++ b/proguard.flags @@ -3,8 +3,8 @@ } -keep class com.android.launcher3.BaseRecyclerViewFastScrollBar { - public void setWidth(int); - public int getWidth(); + public void setThumbWidth(int); + public int getThumbWidth(); public void setTrackAlpha(int); public int getTrackAlpha(); } diff --git a/src/com/android/launcher3/BaseRecyclerView.java b/src/com/android/launcher3/BaseRecyclerView.java index 0fae427e86..f0d8b3b3d2 100644 --- a/src/com/android/launcher3/BaseRecyclerView.java +++ b/src/com/android/launcher3/BaseRecyclerView.java @@ -92,9 +92,15 @@ public abstract class BaseRecyclerView extends RecyclerView // TODO(winsonc): If we want to animate the section heads while scrolling, we can // initiate that here if the recycler view scroll state is not // RecyclerView.SCROLL_STATE_IDLE. + + onUpdateScrollbar(dy); } } + public void reset() { + mScrollbar.reattachThumbToScroll(); + } + @Override protected void onFinishInflate() { super.onFinishInflate(); @@ -143,7 +149,7 @@ public abstract class BaseRecyclerView extends RecyclerView mScrollbar.handleTouchEvent(ev, mDownX, mDownY, mLastY); break; } - return mScrollbar.isDragging(); + return mScrollbar.isDraggingThumb(); } public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { @@ -185,12 +191,10 @@ public abstract class BaseRecyclerView extends RecyclerView * AvailableScrollHeight = Total height of the all items - last page height * * This assumes that all rows are the same height. - * - * @param yOffset the offset from the top of the recycler view to start tracking. */ - protected int getAvailableScrollHeight(int rowCount, int rowHeight, int yOffset) { + protected int getAvailableScrollHeight(int rowCount, int rowHeight) { int visibleHeight = getHeight() - mBackgroundPadding.top - mBackgroundPadding.bottom; - int scrollHeight = getPaddingTop() + yOffset + rowCount * rowHeight + getPaddingBottom(); + int scrollHeight = getPaddingTop() + rowCount * rowHeight + getPaddingBottom(); int availableScrollHeight = scrollHeight - visibleHeight; return availableScrollHeight; } @@ -222,7 +226,7 @@ public abstract class BaseRecyclerView extends RecyclerView @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); - onUpdateScrollbar(); + onUpdateScrollbar(0); mScrollbar.draw(canvas); } @@ -234,24 +238,21 @@ public abstract class BaseRecyclerView extends RecyclerView * @param scrollPosState the current scroll position * @param rowCount the number of rows, used to calculate the total scroll height (assumes that * all rows are the same height) - * @param yOffset the offset to start tracking in the recycler view (only used for all apps) */ protected void synchronizeScrollBarThumbOffsetToViewScroll(ScrollPositionState scrollPosState, - int rowCount, int yOffset) { - int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight, - yOffset); - int availableScrollBarHeight = getAvailableScrollBarHeight(); - + int rowCount) { // Only show the scrollbar if there is height to be scrolled + int availableScrollBarHeight = getAvailableScrollBarHeight(); + int availableScrollHeight = getAvailableScrollHeight(rowCount, scrollPosState.rowHeight); if (availableScrollHeight <= 0) { - mScrollbar.setScrollbarThumbOffset(-1, -1); + mScrollbar.setThumbOffset(-1, -1); return; } // Calculate the current scroll position, the scrollY of the recycler view accounts for the // view padding, while the scrollBarY is drawn right up to the background padding (ignoring // padding) - int scrollY = getPaddingTop() + yOffset + + int scrollY = getPaddingTop() + (scrollPosState.rowIndex * scrollPosState.rowHeight) - scrollPosState.rowTopOffset; int scrollBarY = mBackgroundPadding.top + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); @@ -261,9 +262,9 @@ public abstract class BaseRecyclerView extends RecyclerView if (Utilities.isRtl(getResources())) { scrollBarX = mBackgroundPadding.left; } else { - scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getWidth(); + scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth(); } - mScrollbar.setScrollbarThumbOffset(scrollBarX, scrollBarY); + mScrollbar.setThumbOffset(scrollBarX, scrollBarY); } /** @@ -276,10 +277,15 @@ public abstract class BaseRecyclerView extends RecyclerView * Updates the bounds for the scrollbar. *

Override in each subclass of this base class. */ - public abstract void onUpdateScrollbar(); + public abstract void onUpdateScrollbar(int dy); /** *

Override in each subclass of this base class. */ public void onFastScrollCompleted() {} + + /** + * Returns information about the item that the recycler view is currently scrolled to. + */ + protected abstract void getCurScrollState(ScrollPositionState stateOut); } \ No newline at end of file diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java index 2c4184dc48..29e234d6cb 100644 --- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java +++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java @@ -56,9 +56,12 @@ public class BaseRecyclerViewFastScrollBar { private int mThumbMaxWidth; @Thunk int mThumbWidth; @Thunk int mThumbHeight; + private float mLastTouchY; // The inset is the buffer around which a point will still register as a click on the scrollbar private int mTouchInset; private boolean mIsDragging; + private boolean mIsThumbDetached; + private boolean mCanThumbDetach; // This is the offset from the top of the scrollbar when the user first starts touching. To // prevent jumping, this offset is applied as the user scrolls. @@ -84,7 +87,15 @@ public class BaseRecyclerViewFastScrollBar { mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset); } - public void setScrollbarThumbOffset(int x, int y) { + public void setDetachThumbOnFastScroll() { + mCanThumbDetach = true; + } + + public void reattachThumbToScroll() { + mIsThumbDetached = false; + } + + public void setThumbOffset(int x, int y) { if (mThumbOffset.x == x && mThumbOffset.y == y) { return; } @@ -95,8 +106,12 @@ public class BaseRecyclerViewFastScrollBar { mRv.invalidate(mInvalidateRect); } + public Point getThumbOffset() { + return mThumbOffset; + } + // Setter/getter for the search bar width for animations - public void setWidth(int width) { + public void setThumbWidth(int width) { mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight()); mThumbWidth = width; mInvalidateRect.union(new Rect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, @@ -104,7 +119,7 @@ public class BaseRecyclerViewFastScrollBar { mRv.invalidate(mInvalidateRect); } - public int getWidth() { + public int getThumbWidth() { return mThumbWidth; } @@ -127,10 +142,18 @@ public class BaseRecyclerViewFastScrollBar { return mThumbMaxWidth; } - public boolean isDragging() { + public float getLastTouchY() { + return mLastTouchY; + } + + public boolean isDraggingThumb() { return mIsDragging; } + public boolean isThumbDetached() { + return mIsThumbDetached; + } + /** * Handles the touch event and determines whether to show the fast scroller (or updates it if * it is already showing). @@ -152,6 +175,9 @@ public class BaseRecyclerViewFastScrollBar { Math.abs(y - downY) > config.getScaledTouchSlop()) { mRv.getParent().requestDisallowInterceptTouchEvent(true); mIsDragging = true; + if (mCanThumbDetach) { + mIsThumbDetached = true; + } mTouchOffset += (lastY - downY); mPopup.animateVisibility(true); animateScrollbar(true); @@ -166,11 +192,13 @@ public class BaseRecyclerViewFastScrollBar { mPopup.setSectionName(sectionName); mPopup.animateVisibility(!sectionName.isEmpty()); mRv.invalidate(mPopup.updateFastScrollerBounds(mRv, lastY)); + mLastTouchY = boundedY; } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: mTouchOffset = 0; + mLastTouchY = 0; if (mIsDragging) { mIsDragging = false; mPopup.animateVisibility(false); @@ -205,7 +233,7 @@ public class BaseRecyclerViewFastScrollBar { } ObjectAnimator trackAlphaAnim = ObjectAnimator.ofInt(this, "trackAlpha", isScrolling ? MAX_TRACK_ALPHA : 0); - ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "width", + ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "thumbWidth", isScrolling ? mThumbMaxWidth : mThumbMinWidth); ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), mThumbPaint.getColor(), isScrolling ? mThumbActiveColor : mThumbInactiveColor); diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java index e129dc6d37..6d008ab98f 100644 --- a/src/com/android/launcher3/allapps/AllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java @@ -555,8 +555,9 @@ public class AllAppsContainerView extends BaseContainerView implements DragSourc @Override public void onLauncherTransitionEnd(Launcher l, boolean animated, boolean toWorkspace) { if (toWorkspace) { - // Reset the search bar after transitioning home + // Reset the search bar and base recycler view after transitioning home mSearchBarController.reset(); + mAppsRecyclerView.reset(); } } diff --git a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java index 5aa973a155..5ec8bb258a 100644 --- a/src/com/android/launcher3/allapps/AllAppsRecyclerView.java +++ b/src/com/android/launcher3/allapps/AllAppsRecyclerView.java @@ -72,6 +72,7 @@ public class AllAppsRecyclerView extends BaseRecyclerView public AllAppsRecyclerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr); + mScrollbar.setDetachThumbOnFastScroll(); } /** @@ -168,8 +169,8 @@ public class AllAppsRecyclerView extends BaseRecyclerView } // Map the touch position back to the scroll of the recycler view - getCurScrollState(mScrollPosState, mApps.getAdapterItems()); - int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, 0); + getCurScrollState(mScrollPosState); + int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight); LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager(); if (mFastScrollMode == FAST_SCROLL_MODE_FREE_SCROLL) { layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); @@ -216,24 +217,73 @@ public class AllAppsRecyclerView extends BaseRecyclerView * Updates the bounds for the scrollbar. */ @Override - public void onUpdateScrollbar() { + public void onUpdateScrollbar(int dy) { List items = mApps.getAdapterItems(); // Skip early if there are no items or we haven't been measured if (items.isEmpty() || mNumAppsPerRow == 0) { - mScrollbar.setScrollbarThumbOffset(-1, -1); + mScrollbar.setThumbOffset(-1, -1); return; } // Find the index and height of the first visible row (all rows have the same height) int rowCount = mApps.getNumAppRows(); - getCurScrollState(mScrollPosState, items); + getCurScrollState(mScrollPosState); if (mScrollPosState.rowIndex < 0) { - mScrollbar.setScrollbarThumbOffset(-1, -1); + mScrollbar.setThumbOffset(-1, -1); return; } - synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, 0); + // Only show the scrollbar if there is height to be scrolled + int availableScrollBarHeight = getAvailableScrollBarHeight(); + int availableScrollHeight = getAvailableScrollHeight(mApps.getNumAppRows(), mScrollPosState.rowHeight); + if (availableScrollHeight <= 0) { + mScrollbar.setThumbOffset(-1, -1); + return; + } + + // Calculate the current scroll position, the scrollY of the recycler view accounts for the + // view padding, while the scrollBarY is drawn right up to the background padding (ignoring + // padding) + int scrollY = getPaddingTop() + + (mScrollPosState.rowIndex * mScrollPosState.rowHeight) - mScrollPosState.rowTopOffset; + int scrollBarY = mBackgroundPadding.top + + (int) (((float) scrollY / availableScrollHeight) * availableScrollBarHeight); + + if (mScrollbar.isThumbDetached()) { + int scrollBarX; + if (Utilities.isRtl(getResources())) { + scrollBarX = mBackgroundPadding.left; + } else { + scrollBarX = getWidth() - mBackgroundPadding.right - mScrollbar.getThumbWidth(); + } + + if (mScrollbar.isDraggingThumb()) { + // If the thumb is detached, then just update the thumb to the current + // touch position + mScrollbar.setThumbOffset(scrollBarX, (int) mScrollbar.getLastTouchY()); + } else { + int thumbScrollY = mScrollbar.getThumbOffset().y; + int diffScrollY = scrollBarY - thumbScrollY; + if (diffScrollY * dy > 0f) { + // User is scrolling in the same direction the thumb needs to catch up to the + // current scroll position. + thumbScrollY += dy < 0 ? Math.max(dy, diffScrollY) : Math.min(dy, diffScrollY); + thumbScrollY = Math.max(0, Math.min(availableScrollBarHeight, thumbScrollY)); + mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); + if (scrollBarY == thumbScrollY) { + mScrollbar.reattachThumbToScroll(); + } + } else { + // User is scrolling in an opposite direction to the direction that the thumb + // needs to catch up to the scroll position. Do nothing except for updating + // the scroll bar x to match the thumb width. + mScrollbar.setThumbOffset(scrollBarX, thumbScrollY); + } + } + } else { + synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount); + } } /** @@ -285,13 +335,13 @@ public class AllAppsRecyclerView extends BaseRecyclerView /** * Returns the current scroll state of the apps rows. */ - private void getCurScrollState(ScrollPositionState stateOut, - List items) { + protected void getCurScrollState(ScrollPositionState stateOut) { stateOut.rowIndex = -1; stateOut.rowTopOffset = -1; stateOut.rowHeight = -1; // Return early if there are no items or we haven't been measured + List items = mApps.getAdapterItems(); if (items.isEmpty() || mNumAppsPerRow == 0) { return; } diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java index 396f757906..dac0df12a7 100644 --- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java +++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java @@ -43,6 +43,11 @@ public class AlphabeticalAppsList { private static final boolean DEBUG = false; private static final boolean DEBUG_PREDICTIONS = false; + private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION = 0; + private static final int FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS = 1; + + private final int mFastScrollDistributionMode = FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS; + /** * Info about a section in the alphabetic list */ @@ -85,8 +90,6 @@ public class AlphabeticalAppsList { /** Section & App properties */ // The section for this item public SectionInfo sectionInfo; - // The row that this item shows up on - public int rowIndex; /** App-only properties */ // The section name of this app. Note that there can be multiple items with different @@ -94,6 +97,8 @@ public class AlphabeticalAppsList { public String sectionName = null; // The index of this app in the section public int sectionAppIndex = -1; + // The row that this item shows up on + public int rowIndex; // The index of this app in the row public int rowAppIndex; // The associated AppInfo for the app @@ -188,7 +193,6 @@ public class AlphabeticalAppsList { private int mNumAppsPerRow; private int mNumPredictedAppsPerRow; private int mNumAppRowsInAdapter; - private boolean mDisableEmptyText; public AlphabeticalAppsList(Context context) { mLauncher = (Launcher) context; @@ -215,13 +219,6 @@ public class AlphabeticalAppsList { mAdapter = adapter; } - /** - * Disables the empty text message when there are no search results. - */ - public void disableEmptyText() { - mDisableEmptyText = true; - } - /** * Returns all the apps. */ @@ -523,18 +520,36 @@ public class AlphabeticalAppsList { } mNumAppRowsInAdapter = rowIndex + 1; - // Pre-calculate all the fast scroller fractions based on the number of rows - float rowFraction = 1f / mNumAppRowsInAdapter; - for (FastScrollSectionInfo info : mFastScrollerSections) { - AdapterItem item = info.fastScrollToItem; - if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE && - item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) { - info.touchFraction = 0f; - continue; - } + // Pre-calculate all the fast scroller fractions + switch (mFastScrollDistributionMode) { + case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_ROWS_FRACTION: + float rowFraction = 1f / mNumAppRowsInAdapter; + for (FastScrollSectionInfo info : mFastScrollerSections) { + AdapterItem item = info.fastScrollToItem; + if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE && + item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) { + info.touchFraction = 0f; + continue; + } - float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow); - info.touchFraction = item.rowIndex * rowFraction + subRowFraction; + float subRowFraction = item.rowAppIndex * (rowFraction / mNumAppsPerRow); + info.touchFraction = item.rowIndex * rowFraction + subRowFraction; + } + break; + case FAST_SCROLL_FRACTION_DISTRIBUTE_BY_NUM_SECTIONS: + float perSectionTouchFraction = 1f / mFastScrollerSections.size(); + float cumulativeTouchFraction = 0f; + for (FastScrollSectionInfo info : mFastScrollerSections) { + AdapterItem item = info.fastScrollToItem; + if (item.viewType != AllAppsGridAdapter.ICON_VIEW_TYPE && + item.viewType != AllAppsGridAdapter.PREDICTION_ICON_VIEW_TYPE) { + info.touchFraction = 0f; + continue; + } + info.touchFraction = cumulativeTouchFraction; + cumulativeTouchFraction += perSectionTouchFraction; + } + break; } } diff --git a/src/com/android/launcher3/widget/WidgetsRecyclerView.java b/src/com/android/launcher3/widget/WidgetsRecyclerView.java index 61e63cdb76..e586dc253a 100644 --- a/src/com/android/launcher3/widget/WidgetsRecyclerView.java +++ b/src/com/android/launcher3/widget/WidgetsRecyclerView.java @@ -102,7 +102,7 @@ public class WidgetsRecyclerView extends BaseRecyclerView { getCurScrollState(mScrollPosState); float pos = rowCount * touchFraction; - int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight, 0); + int availableScrollHeight = getAvailableScrollHeight(rowCount, mScrollPosState.rowHeight); LinearLayoutManager layoutManager = ((LinearLayoutManager) getLayoutManager()); layoutManager.scrollToPositionWithOffset(0, (int) -(availableScrollHeight * touchFraction)); @@ -115,29 +115,29 @@ public class WidgetsRecyclerView extends BaseRecyclerView { * Updates the bounds for the scrollbar. */ @Override - public void onUpdateScrollbar() { + public void onUpdateScrollbar(int dy) { int rowCount = mWidgets.getPackageSize(); // Skip early if, there are no items. if (rowCount == 0) { - mScrollbar.setScrollbarThumbOffset(-1, -1); + mScrollbar.setThumbOffset(-1, -1); return; } // Skip early if, there no child laid out in the container. getCurScrollState(mScrollPosState); if (mScrollPosState.rowIndex < 0) { - mScrollbar.setScrollbarThumbOffset(-1, -1); + mScrollbar.setThumbOffset(-1, -1); return; } - synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount, 0); + synchronizeScrollBarThumbOffsetToViewScroll(mScrollPosState, rowCount); } /** * Returns the current scroll state. */ - private void getCurScrollState(ScrollPositionState stateOut) { + protected void getCurScrollState(ScrollPositionState stateOut) { stateOut.rowIndex = -1; stateOut.rowTopOffset = -1; stateOut.rowHeight = -1; From 6779595bec64fa4503c22f75c8e245a449fe665d Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 20 Aug 2015 12:23:52 -0700 Subject: [PATCH 55/76] Updating the scrollbar to make the fastscroller more visible. Bug: 20035978 Change-Id: I56a865262cd07e6af11b4374a7e6354710116dcd --- proguard.flags | 4 +- res/values/colors.xml | 2 +- res/values/dimens.xml | 6 +- .../BaseRecyclerViewFastScrollBar.java | 95 +++++++++++++------ 4 files changed, 70 insertions(+), 37 deletions(-) diff --git a/proguard.flags b/proguard.flags index b4365fee40..e6c4c51c45 100644 --- a/proguard.flags +++ b/proguard.flags @@ -5,8 +5,8 @@ -keep class com.android.launcher3.BaseRecyclerViewFastScrollBar { public void setThumbWidth(int); public int getThumbWidth(); - public void setTrackAlpha(int); - public int getTrackAlpha(); + public void setTrackWidth(int); + public int getTrackWidth(); } -keep class com.android.launcher3.BaseRecyclerViewFastScrollPopup { diff --git a/res/values/colors.xml b/res/values/colors.xml index 0add48cd85..6fe2ae15dc 100644 --- a/res/values/colors.xml +++ b/res/values/colors.xml @@ -39,7 +39,7 @@ #FFFFFFFF - #42000000 + #009688 #009688 diff --git a/res/values/dimens.xml b/res/values/dimens.xml index b9fb6e2575..3f141513f9 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -55,9 +55,9 @@ 4dp - 4dp - 8dp - 64dp + 5dp + 9dp + 72dp -24dp 72dp 48dp diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java index 29e234d6cb..f76aed7ad4 100644 --- a/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java +++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollBar.java @@ -23,6 +23,7 @@ import android.content.res.Resources; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; import android.view.MotionEvent; @@ -51,11 +52,14 @@ public class BaseRecyclerViewFastScrollBar { private int mThumbActiveColor; @Thunk Point mThumbOffset = new Point(-1, -1); @Thunk Paint mThumbPaint; - private Paint mTrackPaint; private int mThumbMinWidth; private int mThumbMaxWidth; @Thunk int mThumbWidth; @Thunk int mThumbHeight; + private int mThumbCurvature; + private Path mThumbPath = new Path(); + private Paint mTrackPaint; + private int mTrackWidth; private float mLastTouchY; // The inset is the buffer around which a point will still register as a click on the scrollbar private int mTouchInset; @@ -75,15 +79,18 @@ public class BaseRecyclerViewFastScrollBar { mPopup = new BaseRecyclerViewFastScrollPopup(rv, res); mTrackPaint = new Paint(); mTrackPaint.setColor(rv.getFastScrollerTrackColor(Color.BLACK)); - mTrackPaint.setAlpha(0); + mTrackPaint.setAlpha(MAX_TRACK_ALPHA); mThumbInactiveColor = rv.getFastScrollerThumbInactiveColor( res.getColor(R.color.container_fastscroll_thumb_inactive_color)); mThumbActiveColor = res.getColor(R.color.container_fastscroll_thumb_active_color); mThumbPaint = new Paint(); + mThumbPaint.setAntiAlias(true); mThumbPaint.setColor(mThumbInactiveColor); + mThumbPaint.setStyle(Paint.Style.FILL); mThumbWidth = mThumbMinWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_min_width); mThumbMaxWidth = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_max_width); mThumbHeight = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_height); + mThumbCurvature = mThumbMaxWidth - mThumbMinWidth; mTouchInset = res.getDimensionPixelSize(R.dimen.container_fastscroll_thumb_touch_inset); } @@ -99,10 +106,12 @@ public class BaseRecyclerViewFastScrollBar { if (mThumbOffset.x == x && mThumbOffset.y == y) { return; } - mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight()); + mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, + mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); mThumbOffset.set(x, y); - mInvalidateRect.union(new Rect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, - mRv.getHeight())); + updateThumbPath(); + mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, + mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); mRv.invalidate(mInvalidateRect); } @@ -110,12 +119,14 @@ public class BaseRecyclerViewFastScrollBar { return mThumbOffset; } - // Setter/getter for the search bar width for animations + // Setter/getter for the thumb bar width for animations public void setThumbWidth(int width) { - mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight()); + mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, + mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); mThumbWidth = width; - mInvalidateRect.union(new Rect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, - mRv.getHeight())); + updateThumbPath(); + mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, mThumbOffset.y, + mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); mRv.invalidate(mInvalidateRect); } @@ -123,15 +134,19 @@ public class BaseRecyclerViewFastScrollBar { return mThumbWidth; } - // Setter/getter for the track background alpha for animations - public void setTrackAlpha(int alpha) { - mTrackPaint.setAlpha(alpha); - mInvalidateRect.set(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight()); + // Setter/getter for the track bar width for animations + public void setTrackWidth(int width) { + mInvalidateRect.set(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth, + mRv.getHeight()); + mTrackWidth = width; + updateThumbPath(); + mInvalidateRect.union(mThumbOffset.x - mThumbCurvature, 0, mThumbOffset.x + mThumbWidth, + mRv.getHeight()); mRv.invalidate(mInvalidateRect); } - public int getTrackAlpha() { - return mTrackPaint.getAlpha(); + public int getTrackWidth() { + return mTrackWidth; } public int getThumbHeight() { @@ -217,8 +232,7 @@ public class BaseRecyclerViewFastScrollBar { if (mTrackPaint.getAlpha() > 0) { canvas.drawRect(mThumbOffset.x, 0, mThumbOffset.x + mThumbWidth, mRv.getHeight(), mTrackPaint); } - canvas.drawRect(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, - mThumbOffset.y + mThumbHeight, mThumbPaint); + canvas.drawPath(mThumbPath, mThumbPaint); // Draw the popup mPopup.draw(canvas); @@ -231,26 +245,45 @@ public class BaseRecyclerViewFastScrollBar { if (mScrollbarAnimator != null) { mScrollbarAnimator.cancel(); } - ObjectAnimator trackAlphaAnim = ObjectAnimator.ofInt(this, "trackAlpha", - isScrolling ? MAX_TRACK_ALPHA : 0); + + mScrollbarAnimator = new AnimatorSet(); + ObjectAnimator trackWidthAnim = ObjectAnimator.ofInt(this, "trackWidth", + isScrolling ? mThumbMaxWidth : mThumbMinWidth); ObjectAnimator thumbWidthAnim = ObjectAnimator.ofInt(this, "thumbWidth", isScrolling ? mThumbMaxWidth : mThumbMinWidth); - ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), - mThumbPaint.getColor(), isScrolling ? mThumbActiveColor : mThumbInactiveColor); - colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animator) { - mThumbPaint.setColor((Integer) animator.getAnimatedValue()); - mRv.invalidate(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, - mThumbOffset.y + mThumbHeight); - } - }); - mScrollbarAnimator = new AnimatorSet(); - mScrollbarAnimator.playTogether(trackAlphaAnim, thumbWidthAnim, colorAnimation); + mScrollbarAnimator.playTogether(trackWidthAnim, thumbWidthAnim); + if (mThumbActiveColor != mThumbInactiveColor) { + ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), + mThumbPaint.getColor(), isScrolling ? mThumbActiveColor : mThumbInactiveColor); + colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator animator) { + mThumbPaint.setColor((Integer) animator.getAnimatedValue()); + mRv.invalidate(mThumbOffset.x, mThumbOffset.y, mThumbOffset.x + mThumbWidth, + mThumbOffset.y + mThumbHeight); + } + }); + mScrollbarAnimator.play(colorAnimation); + } mScrollbarAnimator.setDuration(SCROLL_BAR_VIS_DURATION); mScrollbarAnimator.start(); } + /** + * Updates the path for the thumb drawable. + */ + private void updateThumbPath() { + mThumbCurvature = mThumbMaxWidth - mThumbWidth; + mThumbPath.reset(); + mThumbPath.moveTo(mThumbOffset.x + mThumbWidth, mThumbOffset.y); // tr + mThumbPath.lineTo(mThumbOffset.x + mThumbWidth, mThumbOffset.y + mThumbHeight); // br + mThumbPath.lineTo(mThumbOffset.x, mThumbOffset.y + mThumbHeight); // bl + mThumbPath.cubicTo(mThumbOffset.x, mThumbOffset.y + mThumbHeight, + mThumbOffset.x - mThumbCurvature, mThumbOffset.y + mThumbHeight / 2, + mThumbOffset.x, mThumbOffset.y); // bl2tl + mThumbPath.close(); + } + /** * Returns whether the specified points are near the scroll bar bounds. */ From 81c5f7e39727085e1a8915cc36e9cbf2bb085890 Mon Sep 17 00:00:00 2001 From: Winson Date: Thu, 20 Aug 2015 15:22:18 -0700 Subject: [PATCH 56/76] Fixing issue with search bar not being visible. - If you queue up two SearchBarDropTarget state transitions one right after another, the second animation will skip (since the values haven't started animating yet) and the first animation will complete instead, leaving the visibility of the search and drop target bars at odds with the current bar state. - Instead, we should cancel all existing animators first, such that if we are already in the final state, no animations will run, and the correct bars will be visibile. Bug: 23201830 Change-Id: I1f8e802821ef2b4904a3efa7edbea6ae615479b9 --- src/com/android/launcher3/SearchDropTargetBar.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index 1efdfb62d3..772a334b9e 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -186,7 +186,12 @@ public class SearchDropTargetBar extends FrameLayout implements DragController.D */ private void animateViewAlpha(LauncherViewPropertyAnimator animator, View v, float alpha, int duration) { - if (v != null && Float.compare(v.getAlpha(), alpha) != 0) { + if (v == null) { + return; + } + + animator.cancel(); + if (Float.compare(v.getAlpha(), alpha) != 0) { if (duration > 0) { animator.alpha(alpha).withLayer().setDuration(duration).start(); } else { From ad0c2f28ceb750646e0132a01d393f5115cbfca9 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 21 Aug 2015 09:23:44 -0700 Subject: [PATCH 57/76] Using the usermanager api to get creation time Bug: 22539048 Change-Id: I17647f2456434ca66a9778300e1a2910cec171d4 --- src/com/android/launcher3/compat/UserManagerCompatVL.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/com/android/launcher3/compat/UserManagerCompatVL.java b/src/com/android/launcher3/compat/UserManagerCompatVL.java index 4d404db2bc..dc3ec3cd85 100644 --- a/src/com/android/launcher3/compat/UserManagerCompatVL.java +++ b/src/com/android/launcher3/compat/UserManagerCompatVL.java @@ -26,6 +26,7 @@ import android.os.Build; import android.os.UserHandle; import com.android.launcher3.LauncherAppState; +import com.android.launcher3.Utilities; import com.android.launcher3.util.LongArrayMap; import java.util.ArrayList; @@ -100,7 +101,9 @@ public class UserManagerCompatVL extends UserManagerCompatV17 { @Override public long getUserCreationTime(UserHandleCompat user) { - // TODO: Use system API once available. + if (Utilities.ATLEAST_MARSHMALLOW) { + return mUserManager.getUserCreationTime(user.getUser()); + } SharedPreferences prefs = mContext.getSharedPreferences( LauncherAppState.getSharedPreferencesKey(), Context.MODE_PRIVATE); String key = USER_CREATION_TIME_KEY + getSerialNumberForUser(user); From 830b5e08ef8ccc8e4193926ac10ac38cb0ae3e56 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Fri, 21 Aug 2015 09:26:10 -0700 Subject: [PATCH 58/76] Updating the target sdk to launcher Change-Id: Icf8e22f6ceb57dcd4e101b8b1d3ae86c52d12b30 --- AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 3af38f3847..99a6ad9aa2 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -20,7 +20,7 @@ - + Date: Thu, 20 Aug 2015 16:17:36 -0700 Subject: [PATCH 59/76] Updating theme to use the light theme by default, instead of wallpaper theme > This allows us to use all the goodness of material theme > Cursor in folder edit text is no longer 1px wide Change-Id: I705f5472ff71969ae45747dd127a8ba5253df44d --- WallpaperPicker/res/values-v19/styles.xml | 2 +- WallpaperPicker/res/values-v21/styles.xml | 7 +++++++ WallpaperPicker/res/values/colors.xml | 2 ++ WallpaperPicker/res/values/styles.xml | 8 +++++++- res/layout/all_apps_search_market.xml | 2 +- res/layout/overview_panel.xml | 3 +++ res/layout/user_folder.xml | 1 - 7 files changed, 21 insertions(+), 4 deletions(-) diff --git a/WallpaperPicker/res/values-v19/styles.xml b/WallpaperPicker/res/values-v19/styles.xml index 136cf012ca..15fb0ea2a7 100644 --- a/WallpaperPicker/res/values-v19/styles.xml +++ b/WallpaperPicker/res/values-v19/styles.xml @@ -25,7 +25,7 @@ true - diff --git a/WallpaperPicker/res/values-v21/styles.xml b/WallpaperPicker/res/values-v21/styles.xml index 582ab8fed8..70220edb9b 100644 --- a/WallpaperPicker/res/values-v21/styles.xml +++ b/WallpaperPicker/res/values-v21/styles.xml @@ -33,4 +33,11 @@ ?android:attr/selectableItemBackgroundBorderless + \ No newline at end of file diff --git a/WallpaperPicker/res/values/colors.xml b/WallpaperPicker/res/values/colors.xml index adae7cff6f..6ba32f06d9 100644 --- a/WallpaperPicker/res/values/colors.xml +++ b/WallpaperPicker/res/values/colors.xml @@ -19,4 +19,6 @@ --> #66000000 + + #ff009688 diff --git a/WallpaperPicker/res/values/styles.xml b/WallpaperPicker/res/values/styles.xml index 74aeab9032..d1c945a32a 100644 --- a/WallpaperPicker/res/values/styles.xml +++ b/WallpaperPicker/res/values/styles.xml @@ -35,9 +35,15 @@ #88000000 - + +