From f158286c3481475b36c20d8b189e2837d5562813 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 5 Sep 2024 16:49:10 -0700 Subject: [PATCH] Adding more properties in LaunchCookie, instead of just itemId This allows better matching of items who do not have a fixed id Bug: 364363493 Flag: EXEMPT bug fix Test: Manual- Verified app launch and back animation Change-Id: I4f9deba89399b89974686b64226b99f91a3d46be --- .../launcher3/QuickstepTransitionManager.java | 22 +++----- .../QuickstepInteractionHandler.java | 5 +- .../uioverrides/QuickstepLauncher.java | 39 ------------- .../quickstep/LauncherSwipeHandlerV2.java | 16 +----- src/com/android/launcher3/Launcher.java | 13 +++-- .../launcher3/model/data/ItemInfo.java | 10 +++- .../android/launcher3/util/StableViewInfo.kt | 56 +++++++++++++++++++ .../launcher3/views/FloatingSurfaceView.java | 3 +- 8 files changed, 85 insertions(+), 79 deletions(-) create mode 100644 src/com/android/launcher3/util/StableViewInfo.kt diff --git a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java index e51c956016..621d2263e9 100644 --- a/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java +++ b/quickstep/src/com/android/launcher3/QuickstepTransitionManager.java @@ -56,7 +56,6 @@ import static com.android.launcher3.config.FeatureFlags.ENABLE_BACK_SWIPE_HOME_A import static com.android.launcher3.config.FeatureFlags.ENABLE_SCRIM_FOR_APP_LAUNCH; import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION; import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY; -import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.testing.shared.TestProtocol.WALLPAPER_OPEN_ANIMATION_FINISHED_MESSAGE; import static com.android.launcher3.util.DisplayController.isTransientTaskbar; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; @@ -138,8 +137,8 @@ import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.uioverrides.QuickstepLauncher; import com.android.launcher3.util.ActivityOptionsWrapper; import com.android.launcher3.util.DynamicResource; -import com.android.launcher3.util.ObjectWrapper; import com.android.launcher3.util.RunnableList; +import com.android.launcher3.util.StableViewInfo; import com.android.launcher3.util.Themes; import com.android.launcher3.views.FloatingIconView; import com.android.launcher3.views.ScrimView; @@ -174,6 +173,7 @@ import com.android.wm.shell.startingsurface.IStartingWindowListener; import java.io.PrintWriter; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -434,7 +434,7 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener */ private void addLaunchCookie(IBinder cookie, ItemInfo info, ActivityOptions options) { if (cookie == null) { - cookie = mLauncher.getLaunchCookie(info); + cookie = StableViewInfo.toLaunchCookie(info); } if (cookie != null) { @@ -1433,20 +1433,12 @@ public class QuickstepTransitionManager implements OnDeviceProfileChangeListener // Find the associated item info for the launch cookie (if available), note that predicted // apps actually have an id of -1, so use another default id here - final ArrayList launchCookies = runningTaskTarget.taskInfo.launchCookies == null - ? new ArrayList<>() + final List launchCookies = runningTaskTarget.taskInfo.launchCookies == null + ? Collections.EMPTY_LIST : runningTaskTarget.taskInfo.launchCookies; - int launchCookieItemId = NO_MATCHING_ID; - for (IBinder cookie : launchCookies) { - Integer itemId = ObjectWrapper.unwrap(cookie); - if (itemId != null) { - launchCookieItemId = itemId; - break; - } - } - - return mLauncher.getFirstMatchForAppClose(launchCookieItemId, packageName, + return mLauncher.getFirstMatchForAppClose( + StableViewInfo.fromLaunchCookies(launchCookies), packageName, UserHandle.of(runningTaskTarget.taskInfo.userId), true /* supportsAllAppsState */); } diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java index 039c0a0ad8..5b07926031 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java @@ -34,6 +34,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.util.ActivityOptionsWrapper; +import com.android.launcher3.util.StableViewInfo; import com.android.launcher3.widget.LauncherAppWidgetHostView; /** Provides a Quickstep specific animation when launching an activity from an app widget. */ @@ -69,8 +70,8 @@ class QuickstepInteractionHandler implements RemoteViews.InteractionHandler { .getActivityLaunchOptions(hostView); Object itemInfo = hostView.getTag(); IBinder launchCookie = null; - if (itemInfo instanceof ItemInfo) { - launchCookie = mLauncher.getLaunchCookie((ItemInfo) itemInfo); + if (itemInfo instanceof ItemInfo info) { + launchCookie = StableViewInfo.toLaunchCookie(info); activityOptions.options.setLaunchCookie(launchCookie); } if (Utilities.ATLEAST_S && !pendingIntent.isActivity()) { diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 0b385d9a32..b5b8909532 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -41,7 +41,6 @@ import static com.android.launcher3.config.FeatureFlags.enableSplitContextually; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_HOME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SPLIT_SELECTION_EXIT_INTERRUPTED; -import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.popup.QuickstepSystemShortcut.getSplitSelectShortcutByPosition; import static com.android.launcher3.popup.SystemShortcut.APP_INFO; import static com.android.launcher3.popup.SystemShortcut.BUBBLE_SHORTCUT; @@ -83,7 +82,6 @@ import android.hardware.display.DisplayManager; import android.media.permission.SafeCloseable; import android.os.Build; import android.os.Bundle; -import android.os.IBinder; import android.os.IRemoteCallback; import android.os.SystemProperties; import android.os.Trace; @@ -1232,43 +1230,6 @@ public class QuickstepLauncher extends Launcher implements RecentsViewContainer, mSplitWithKeyboardShortcutController.enterStageSplit(leftOrTop); } - /** - * Return a new launch cookie for the activity launch if supported. - * - * @param info the item info for the launch - */ - public IBinder getLaunchCookie(ItemInfo info) { - if (info == null) { - return null; - } - switch (info.container) { - case Favorites.CONTAINER_DESKTOP: - case Favorites.CONTAINER_HOTSEAT: - case Favorites.CONTAINER_PRIVATESPACE: - // Fall through and continue it's on the workspace (we don't support swiping back - // to other containers like all apps or the hotseat predictions (which can change) - break; - default: - if (info.container >= 0) { - // Also allow swiping to folders - break; - } - // Reset any existing launch cookies associated with the cookie - return ObjectWrapper.wrap(NO_MATCHING_ID); - } - switch (info.itemType) { - case Favorites.ITEM_TYPE_APPLICATION: - case Favorites.ITEM_TYPE_DEEP_SHORTCUT: - case Favorites.ITEM_TYPE_APPWIDGET: - // Fall through and continue if it's an app, shortcut, or widget - break; - default: - // Reset any existing launch cookies associated with the cookie - return ObjectWrapper.wrap(NO_MATCHING_ID); - } - return ObjectWrapper.wrap(new Integer(info.id)); - } - @Override public void onDisplayInfoChanged(Context context, DisplayController.Info info, int flags) { super.onDisplayInfoChanged(context, info, flags); diff --git a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java index f653e60e4b..d2dcd7bb1e 100644 --- a/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java +++ b/quickstep/src/com/android/quickstep/LauncherSwipeHandlerV2.java @@ -20,7 +20,6 @@ import static com.android.app.animation.Interpolators.LINEAR; import static com.android.launcher3.Flags.enableScalingRevealHomeAnimation; import static com.android.launcher3.LauncherState.NORMAL; import static com.android.launcher3.Utilities.mapBoundToRange; -import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION; import static com.android.launcher3.views.FloatingIconView.getFloatingIconView; @@ -42,7 +41,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.states.StateAnimationConfig; import com.android.launcher3.uioverrides.QuickstepLauncher; -import com.android.launcher3.util.ObjectWrapper; +import com.android.launcher3.util.StableViewInfo; import com.android.launcher3.views.ClipIconView; import com.android.launcher3.views.FloatingIconView; import com.android.launcher3.views.FloatingView; @@ -301,18 +300,7 @@ public class LauncherSwipeHandlerV2 extends AbsSwipeUpHandler< return null; } - // Find the associated item info for the launch cookie (if available), note that predicted - // apps actually have an id of -1, so use another default id here - int launchCookieItemId = NO_MATCHING_ID; - for (IBinder cookie : launchCookies) { - Integer itemId = ObjectWrapper.unwrap(cookie); - if (itemId != null) { - launchCookieItemId = itemId; - break; - } - } - - return mContainer.getFirstMatchForAppClose(launchCookieItemId, + return mContainer.getFirstMatchForAppClose(StableViewInfo.fromLaunchCookies(launchCookies), sourceTaskView.getFirstTask().key.getComponent().getPackageName(), UserHandle.of(sourceTaskView.getFirstTask().key.userId), false /* supportsAllAppsState */); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index bafb528dd1..961651ea42 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -241,6 +241,7 @@ import com.android.launcher3.util.RunnableList; import com.android.launcher3.util.ScreenOnTracker; import com.android.launcher3.util.ScreenOnTracker.ScreenOnListener; import com.android.launcher3.util.SettingsCache; +import com.android.launcher3.util.StableViewInfo; import com.android.launcher3.util.SystemUiController; import com.android.launcher3.util.Themes; import com.android.launcher3.util.Thunk; @@ -2424,17 +2425,16 @@ public class Launcher extends StatefulActivity * 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, - * or ItemInfo#NO_MATCHING_ID if you want to not match by item id + * @param svi The StableViewInfo of the preferred item to match to if it exists or null * @param packageName The package name of the app to match. * @param user The user of the app to match. * @param supportsAllAppsState If true and we are in All Apps state, looks for view in All Apps. * Else we only looks on the workspace. */ - public @Nullable View getFirstMatchForAppClose(int preferredItemId, String packageName, + public @Nullable View getFirstMatchForAppClose( + @Nullable StableViewInfo svi, String packageName, UserHandle user, boolean supportsAllAppsState) { - final Predicate preferredItem = info -> - info != null && info.id == preferredItemId; + final Predicate preferredItem = svi == null ? i -> false : svi::matches; final Predicate packageAndUserAndApp = info -> info != null && info.itemType == ITEM_TYPE_APPLICATION @@ -2525,6 +2525,9 @@ public class Launcher extends StatefulActivity final int itemCount = container.getChildCount(); for (int itemIdx = 0; itemIdx < itemCount; itemIdx++) { View item = container.getChildAt(itemIdx); + if (item.getVisibility() != View.VISIBLE) { + continue; + } if (item instanceof ViewGroup viewGroup) { View view = mapOverViewGroup(viewGroup, op); if (view != null) { diff --git a/src/com/android/launcher3/model/data/ItemInfo.java b/src/com/android/launcher3/model/data/ItemInfo.java index 62198cbb69..b706d249f2 100644 --- a/src/com/android/launcher3/model/data/ItemInfo.java +++ b/src/com/android/launcher3/model/data/ItemInfo.java @@ -81,8 +81,6 @@ public class ItemInfo { public static final boolean DEBUG = false; public static final int NO_ID = -1; - // An id that doesn't match any item, including predicted apps with have an id=NO_ID - public static final int NO_MATCHING_ID = Integer.MIN_VALUE; /** Hidden field Settings.Secure.NAV_BAR_KIDS_MODE */ private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor("nav_bar_kids_mode"); @@ -545,6 +543,14 @@ public class ItemInfo { this.title = title; } + /** + * Returns a string ID that is stable for a user session, but may not be persisted + */ + @Nullable + public Object getStableId() { + return getComponentKey(); + } + private int getUserType(UserIconInfo info) { if (info == null) { return SysUiStatsLog.LAUNCHER_UICHANGED__USER_TYPE__TYPE_UNKNOWN; diff --git a/src/com/android/launcher3/util/StableViewInfo.kt b/src/com/android/launcher3/util/StableViewInfo.kt new file mode 100644 index 0000000000..29dcd59711 --- /dev/null +++ b/src/com/android/launcher3/util/StableViewInfo.kt @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.util + +import android.os.IBinder +import com.android.launcher3.model.data.ItemInfo +import com.android.launcher3.model.data.ItemInfo.NO_ID + +/** Info parameters that can be used to identify a Launcher object */ +data class StableViewInfo(val itemId: Int, val containerId: Int, val stableId: Any) { + + fun matches(info: ItemInfo?) = + info != null && + itemId == info.id && + containerId == info.container && + stableId == info.stableId + + companion object { + + private fun ItemInfo.toStableViewInfo() = + stableId?.let { sId -> + if (id != NO_ID || container != NO_ID) StableViewInfo(id, container, sId) else null + } + + /** + * Return a new launch cookie for the activity launch if supported. + * + * @param info the item info for the launch + */ + @JvmStatic + fun toLaunchCookie(info: ItemInfo?) = + info?.toStableViewInfo()?.let { ObjectWrapper.wrap(it) } + + /** + * Unwraps the binder and returns the first non-null StableViewInfo in the list or null if + * none can be found + */ + @JvmStatic + fun fromLaunchCookies(launchCookies: List) = + launchCookies.firstNotNullOfOrNull { ObjectWrapper.unwrap(it) } + } +} diff --git a/src/com/android/launcher3/views/FloatingSurfaceView.java b/src/com/android/launcher3/views/FloatingSurfaceView.java index 7fa751772e..5f8e2c0973 100644 --- a/src/com/android/launcher3/views/FloatingSurfaceView.java +++ b/src/com/android/launcher3/views/FloatingSurfaceView.java @@ -15,7 +15,6 @@ */ package com.android.launcher3.views; -import static com.android.launcher3.model.data.ItemInfo.NO_MATCHING_ID; import static com.android.launcher3.views.FloatingIconView.getLocationBoundsForView; import static com.android.launcher3.views.FloatingIconViewCompanion.setPropertiesVisible; @@ -160,7 +159,7 @@ public class FloatingSurfaceView extends AbstractFloatingView implements if (mContract == null) { return; } - View icon = mLauncher.getFirstMatchForAppClose(NO_MATCHING_ID, + View icon = mLauncher.getFirstMatchForAppClose(null /* StableViewInfo */, mContract.componentName.getPackageName(), mContract.user, false /* supportsAllAppsState */);