From c4a99bff04c54283516588c1f692504020a63c09 Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Thu, 4 Nov 2021 16:53:45 -0700 Subject: [PATCH] Play return to icon animation if user swipes back to All Apps. Bug: 205187702 Test: open app in all apps, swipe back to go back to all apps Change-Id: I9e79c8365fdf667321343aaac2f8d77cb60a316a --- .../launcher3/QuickstepTransitionManager.java | 64 +++++++-------- .../quickstep/LauncherSwipeHandlerV2.java | 2 +- src/com/android/launcher3/Launcher.java | 78 +++++++++++++++++++ src/com/android/launcher3/Workspace.java | 75 ------------------ .../launcher3/util/ItemInfoMatcher.java | 13 +++- .../launcher3/views/FloatingSurfaceView.java | 4 +- 6 files changed, 125 insertions(+), 111 deletions(-) diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index 51d7914ee8..0123c4f5b1 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -1214,14 +1214,14 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } /** - * Returns view on the workspace that corresponds to the closing app in the list of app targets + * Returns view on launcher that corresponds to the closing app in the list of app targets */ - private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat[] appTargets) { + private @Nullable View findLauncherView(RemoteAnimationTargetCompat[] appTargets) { for (RemoteAnimationTargetCompat appTarget : appTargets) { if (appTarget.mode == MODE_CLOSING) { - View workspaceView = findWorkspaceView(appTarget); - if (workspaceView != null) { - return workspaceView; + View launcherView = findLauncherView(appTarget); + if (launcherView != null) { + return launcherView; } } } @@ -1229,9 +1229,9 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } /** - * Returns view on the workspace that corresponds to the {@param runningTaskTarget}. + * Returns view on launcher that corresponds to the {@param runningTaskTarget}. */ - private @Nullable View findWorkspaceView(RemoteAnimationTargetCompat runningTaskTarget) { + private @Nullable View findLauncherView(RemoteAnimationTargetCompat runningTaskTarget) { if (runningTaskTarget == null || runningTaskTarget.taskInfo == null) { return null; } @@ -1269,7 +1269,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } } - return mLauncher.getWorkspace().getFirstMatchForAppClose(launchCookieItemId, + return mLauncher.getFirstMatchForAppClose(launchCookieItemId, packageName, UserHandle.of(runningTaskTarget.taskInfo.userId)); } @@ -1292,7 +1292,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener * Closing animator that animates the window into its final location on the workspace. */ private void getClosingWindowAnimators(AnimatorSet animation, - RemoteAnimationTargetCompat[] targets, View workspaceView) { + RemoteAnimationTargetCompat[] targets, View launcherView, PointF velocityPxPerS) { FloatingIconView floatingIconView = null; FloatingWidgetView floatingWidget = null; RectF targetRect = new RectF(); @@ -1308,17 +1308,17 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } // Get floating view and target rect. - if (workspaceView instanceof LauncherAppWidgetHostView) { + if (launcherView instanceof LauncherAppWidgetHostView) { Size windowSize = new Size(mDeviceProfile.availableWidthPx, mDeviceProfile.availableHeightPx); int fallbackBackgroundColor = FloatingWidgetView.getDefaultBackgroundColor(mLauncher, runningTaskTarget); floatingWidget = FloatingWidgetView.getFloatingWidgetView(mLauncher, - (LauncherAppWidgetHostView) workspaceView, targetRect, windowSize, + (LauncherAppWidgetHostView) launcherView, targetRect, windowSize, mDeviceProfile.isMultiWindowMode ? 0 : getWindowCornerRadius(mLauncher), isTransluscent, fallbackBackgroundColor); - } else if (workspaceView != null) { - floatingIconView = getFloatingIconView(mLauncher, workspaceView, + } else if (launcherView != null) { + floatingIconView = getFloatingIconView(mLauncher, launcherView, true /* hideOriginal */, targetRect, false /* isOpening */); } else { targetRect.set(getDefaultWindowTargetRect()); @@ -1373,15 +1373,10 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener } // Use a fixed velocity to start the animation. - float velocityPxPerS = DynamicResource.provider(mLauncher) - .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); - PointF velocity = new PointF(0, -velocityPxPerS); - animation.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, - true /* animateOverviewScrim */, workspaceView).getAnimators()); animation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - anim.start(mLauncher, velocity); + anim.start(mLauncher, velocityPxPerS); } }); } @@ -1556,22 +1551,30 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener if (anim == null) { anim = new AnimatorSet(); - View workspaceView = findWorkspaceView(appTargets); - boolean isWorkspaceViewVisible = workspaceView != null - && !mLauncher.isInState(LauncherState.ALL_APPS) - && !mLauncher.getWorkspace().isOverlayShown(); - boolean playFallBackAnimation = !isWorkspaceViewVisible - && (launcherIsATargetWithMode(appTargets, MODE_OPENING) - || mLauncher.isForceInvisible()); + final boolean launcherIsForceInvisibleOrOpening = mLauncher.isForceInvisible() + || launcherIsATargetWithMode(appTargets, MODE_OPENING); + + View launcherView = findLauncherView(appTargets); + boolean playFallBackAnimation = (launcherView == null + && launcherIsForceInvisibleOrOpening) + || mLauncher.getWorkspace().isOverlayShown(); boolean playWorkspaceReveal = true; if (mFromUnlock) { anim.play(getUnlockWindowAnimator(appTargets, wallpaperTargets)); } else if (ENABLE_BACK_SWIPE_HOME_ANIMATION.get() && !playFallBackAnimation) { - getClosingWindowAnimators(anim, appTargets, workspaceView); - // We play StaggeredWorkspaceAnim as a part of the closing window animation. - playWorkspaceReveal = false; + // Use a fixed velocity to start the animation. + float velocityPxPerS = DynamicResource.provider(mLauncher) + .getDimension(R.dimen.unlock_staggered_velocity_dp_per_s); + PointF velocity = new PointF(0, -velocityPxPerS); + getClosingWindowAnimators(anim, appTargets, launcherView, velocity); + if (!mLauncher.isInState(LauncherState.ALL_APPS)) { + anim.play(new StaggeredWorkspaceAnim(mLauncher, velocity.y, + true /* animateOverviewScrim */, launcherView).getAnimators()); + // We play StaggeredWorkspaceAnim as a part of the closing window animation. + playWorkspaceReveal = false; + } } else { anim.play(getFallbackClosingWindowAnimators(appTargets)); } @@ -1584,8 +1587,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // targets list because it is already visible). In that case, we force // invisibility on touch down, and only reset it after the animation to home // is initialized. - if (launcherIsATargetWithMode(appTargets, MODE_OPENING) - || mLauncher.isForceInvisible()) { + if (launcherIsForceInvisibleOrOpening) { addCujInstrumentation( anim, InteractionJankMonitorWrapper.CUJ_APP_CLOSE_TO_HOME); // Only register the content animation for cancellation when state changes diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index 8a30aad90a..8415665af8 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -264,7 +264,7 @@ public class LauncherSwipeHandlerV2 extends } } - return mActivity.getWorkspace().getFirstMatchForAppClose(launchCookieItemId, + return mActivity.getFirstMatchForAppClose(launchCookieItemId, runningTaskView.getTask().key.getComponent().getPackageName(), UserHandle.of(runningTaskView.getTask().key.userId)); } diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 8154168e4f..d60cd311f9 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -29,6 +29,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_REBIND_SAFE; import static com.android.launcher3.AbstractFloatingView.TYPE_SNACKBAR; import static com.android.launcher3.AbstractFloatingView.getTopOpenViewWithType; import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY; +import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION; import static com.android.launcher3.LauncherState.ALL_APPS; import static com.android.launcher3.LauncherState.FLAG_CLOSE_POPUPS; import static com.android.launcher3.LauncherState.FLAG_MULTI_PAGE; @@ -39,6 +40,7 @@ import static com.android.launcher3.LauncherState.NO_SCALE; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.Utilities.postAsyncCallback; import static com.android.launcher3.accessibility.LauncherAccessibilityDelegate.getSupportedActions; +import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_LAUNCHER_LOAD; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_BACKGROUND; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; @@ -55,6 +57,7 @@ import static com.android.launcher3.popup.SystemShortcut.INSTALL; import static com.android.launcher3.popup.SystemShortcut.WIDGETS; import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK; import static com.android.launcher3.states.RotationHelper.REQUEST_NONE; +import static com.android.launcher3.util.ItemInfoMatcher.forFolderMatch; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; @@ -90,6 +93,7 @@ import android.os.Process; import android.os.StrictMode; import android.os.SystemClock; import android.os.Trace; +import android.os.UserHandle; import android.text.TextUtils; import android.text.method.TextKeyListener; import android.util.Log; @@ -215,6 +219,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -2674,6 +2679,79 @@ public class Launcher extends StatefulActivity implements Launche } } + /** + * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close + * animation. + * + * @param preferredItemId The id of the preferred item to match to if it exists. + * @param packageName The package name of the app to match. + * @param user The user of the app to match. + */ + public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) { + final ItemInfoMatcher preferredItem = (info, cn) -> + info != null && info.id == preferredItemId; + final ItemInfoMatcher packageAndUserAndApp = (info, cn) -> + info != null + && info.itemType == ITEM_TYPE_APPLICATION + && info.user.equals(user) + && info.getTargetComponent() != null + && TextUtils.equals(info.getTargetComponent().getPackageName(), + packageName); + + if (isInState(LauncherState.ALL_APPS)) { + return getFirstMatch(Collections.singletonList(mAppsView.getActiveRecyclerView()), + preferredItem, packageAndUserAndApp); + } else { + List containers = new ArrayList<>(mWorkspace.getPanelCount() + 1); + containers.add(mWorkspace.getHotseat().getShortcutsAndWidgets()); + mWorkspace.forEachVisiblePage(page + -> containers.add(((CellLayout) page).getShortcutsAndWidgets())); + + // Order: Preferred item by itself or in folder, then by matching package/user + if (ADAPTIVE_ICON_WINDOW_ANIM.get()) { + return getFirstMatch(containers, preferredItem, forFolderMatch(preferredItem), + packageAndUserAndApp, forFolderMatch(packageAndUserAndApp)); + } else { + // Do not use Folder as a criteria, since it'll cause a crash when trying to draw + // FolderAdaptiveIcon as the background. + return getFirstMatch(containers, preferredItem, packageAndUserAndApp); + } + } + } + + /** + * Finds the first view matching the ordered operators across the given viewgroups in order. + * @param containers List of ViewGroups to scan, in order of preference. + * @param operators List of operators, in order starting from best matching operator. + */ + private static View getFirstMatch(Iterable containers, + final ItemInfoMatcher... operators) { + for (ItemInfoMatcher operator : operators) { + for (ViewGroup container : containers) { + View match = mapOverViewGroup(container, operator); + if (match != null) { + return match; + } + } + } + return null; + } + + /** + * Returns the first view matching the operator in the given ViewGroups, or null if none. + * Forward iteration matters. + */ + private static View mapOverViewGroup(ViewGroup container, ItemInfoMatcher op) { + final int itemCount = container.getChildCount(); + for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { + View item = container.getChildAt(itemIdx); + if (op.matchesInfo((ItemInfo) item.getTag())) { + return item; + } + } + return null; + } + private ValueAnimator createNewAppBounceAnimation(View v, int i) { ValueAnimator bounceAnim = new PropertyListBuilder().alpha(1).scale(1).build(v) .setDuration(ItemInstallQueue.NEW_SHORTCUT_BOUNCE_DURATION); diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 131fbfb071..fbaf71bc4e 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -26,7 +26,6 @@ import static com.android.launcher3.LauncherState.HINT_STATE; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.LauncherState.SPRING_LOADED; import static com.android.launcher3.anim.AnimatorListeners.forSuccessCallback; -import static com.android.launcher3.config.FeatureFlags.ADAPTIVE_ICON_WINDOW_ANIM; import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_OVERLAY; import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT; @@ -51,7 +50,6 @@ import android.os.Handler; import android.os.Message; import android.os.Parcelable; import android.os.UserHandle; -import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -3148,62 +3146,6 @@ public class Workspace extends PagedView return layouts; } - /** - * Similar to {@link #getFirstMatch} but optimized to finding a suitable view for the app close - * animation. - * - * @param preferredItemId The id of the preferred item to match to if it exists. - * @param packageName The package name of the app to match. - * @param user The user of the app to match. - */ - public View getFirstMatchForAppClose(int preferredItemId, String packageName, UserHandle user) { - final ItemOperator preferredItem = (ItemInfo info, View view) -> - info != null && info.id == preferredItemId; - final ItemOperator preferredItemInFolder = (info, view) -> { - if (info instanceof FolderInfo) { - FolderInfo folderInfo = (FolderInfo) info; - for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) { - if (preferredItem.evaluate(shortcutInfo, view)) { - return true; - } - } - } - return false; - }; - final ItemOperator packageAndUserAndApp = (ItemInfo info, View view) -> - info != null - && info.itemType == ITEM_TYPE_APPLICATION - && info.user.equals(user) - && info.getTargetComponent() != null - && TextUtils.equals(info.getTargetComponent().getPackageName(), - packageName); - final ItemOperator packageAndUserAndAppInFolder = (info, view) -> { - if (info instanceof FolderInfo) { - FolderInfo folderInfo = (FolderInfo) info; - for (WorkspaceItemInfo shortcutInfo : folderInfo.contents) { - if (packageAndUserAndApp.evaluate(shortcutInfo, view)) { - return true; - } - } - } - return false; - }; - - List cellLayouts = new ArrayList<>(getPanelCount() + 1); - cellLayouts.add(getHotseat()); - forEachVisiblePage(page -> cellLayouts.add((CellLayout) page)); - - // Order: Preferred item, App icons in hotseat/workspace, app in folder in hotseat/workspace - if (ADAPTIVE_ICON_WINDOW_ANIM.get()) { - return getFirstMatch(cellLayouts, preferredItem, preferredItemInFolder, - packageAndUserAndApp, packageAndUserAndAppInFolder); - } else { - // Do not use Folder as a criteria, since it'll cause a crash when trying to draw - // FolderAdaptiveIcon as the background. - return getFirstMatch(cellLayouts, preferredItem, packageAndUserAndApp); - } - } - public View getHomescreenIconByItemId(final int id) { return getFirstMatch((info, v) -> info != null && info.id == id); } @@ -3229,23 +3171,6 @@ public class Workspace extends PagedView return value[0]; } - /** - * Finds the first view matching the ordered operators across the given cell layouts by order. - * @param cellLayouts List of CellLayouts to scan, in order of preference. - * @param operators List of operators, in order starting from best matching operator. - */ - View getFirstMatch(Iterable cellLayouts, final ItemOperator... operators) { - for (ItemOperator operator : operators) { - for (CellLayout cellLayout : cellLayouts) { - View match = mapOverCellLayout(cellLayout, operator); - if (match != null) { - return match; - } - } - } - return null; - } - void clearDropTargets() { mapOverItems(new ItemOperator() { @Override diff --git a/src/com/android/launcher3/util/ItemInfoMatcher.java b/src/com/android/launcher3/util/ItemInfoMatcher.java index ab3083de0f..7917410b3b 100644 --- a/src/com/android/launcher3/util/ItemInfoMatcher.java +++ b/src/com/android/launcher3/util/ItemInfoMatcher.java @@ -20,6 +20,7 @@ import android.content.ComponentName; import android.os.UserHandle; import com.android.launcher3.LauncherSettings.Favorites; +import com.android.launcher3.model.data.FolderInfo; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.shortcuts.ShortcutKey; @@ -85,8 +86,16 @@ public interface ItemInfoMatcher { } static ItemInfoMatcher ofShortcutKeys(Set keys) { - return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT && - keys.contains(ShortcutKey.fromItemInfo(info)); + return (info, cn) -> info.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT + && keys.contains(ShortcutKey.fromItemInfo(info)); + } + + /** + * Returns a matcher for items within folders. + */ + static ItemInfoMatcher forFolderMatch(ItemInfoMatcher childOperator) { + return (info, cn) -> info instanceof FolderInfo && ((FolderInfo) info).contents.stream() + .anyMatch(childOperator::matchesInfo); } /** diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java index e2e3be7976..f32f904981 100644 --- a/src/com/android/launcher3/views/FloatingSurfaceView.java +++ b/src/com/android/launcher3/views/FloatingSurfaceView.java @@ -158,7 +158,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements if (mContract == null) { return; } - View icon = mLauncher.getWorkspace().getFirstMatchForAppClose(-1, + View icon = mLauncher.getFirstMatchForAppClose(-1, mContract.componentName.getPackageName(), mContract.user); boolean iconChanged = mIcon != icon; @@ -182,7 +182,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements lp.topMargin = Math.round(mIconPosition.top); } } - if (iconChanged && !mIconBounds.isEmpty()) { + if (mIcon != null && iconChanged && !mIconBounds.isEmpty()) { // Record the icon display setCurrentIconVisible(true); Canvas c = mPicture.beginRecording(mIconBounds.width(), mIconBounds.height());