From d7778831c567eb3892fadfa917df1ef0f743891e Mon Sep 17 00:00:00 2001 From: Schneider Victor-tulias Date: Wed, 26 Oct 2022 11:01:26 -0700 Subject: [PATCH 01/14] Improve RecentsModel log dump for ease-of-use and readability. Test: launched several apps, switched between apps and dumped logs Bug: 255147097 Change-Id: I6319d7027ece9e3f244ebb6a82b260724f94096b --- .../android/quickstep/RecentTasksList.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java index 7bcc661af7..d46565bd17 100644 --- a/quickstep/src/com/android/quickstep/RecentTasksList.java +++ b/quickstep/src/com/android/quickstep/RecentTasksList.java @@ -23,6 +23,8 @@ import static com.android.wm.shell.util.GroupedRecentTaskInfo.TYPE_FREEFORM; import android.annotation.TargetApi; import android.app.ActivityManager; import android.app.KeyguardManager; +import android.app.TaskInfo; +import android.content.ComponentName; import android.os.Build; import android.os.Process; import android.os.RemoteException; @@ -324,8 +326,14 @@ public class RecentTasksList { writer.println(prefix + " mChangeId=" + mChangeId); writer.println(prefix + " mResultsUi=[id=" + mResultsUi.mRequestId + ", tasks="); for (GroupTask task : mResultsUi) { - writer.println(prefix + " t1=" + task.task1.key.id - + " t2=" + (task.hasMultipleTasks() ? task.task2.key.id : "-1")); + Task task1 = task.task1; + Task task2 = task.task2; + ComponentName cn1 = task1.getTopComponent(); + ComponentName cn2 = task2 != null ? task2.getTopComponent() : null; + writer.println(prefix + " t1: (id=" + task1.key.id + + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)") + + " t2: (id=" + (task2 != null ? task2.key.id : "-1") + + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)")); } writer.println(prefix + " ]"); int currentUserId = Process.myUserHandle().getIdentifier(); @@ -333,8 +341,14 @@ public class RecentTasksList { mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId); writer.println(prefix + " rawTasks=["); for (GroupedRecentTaskInfo task : rawTasks) { - writer.println(prefix + " t1=" + task.getTaskInfo1().taskId - + " t2=" + (task.getTaskInfo2() != null ? task.getTaskInfo2().taskId : "-1")); + TaskInfo taskInfo1 = task.getTaskInfo1(); + TaskInfo taskInfo2 = task.getTaskInfo2(); + ComponentName cn1 = taskInfo1.topActivity; + ComponentName cn2 = taskInfo2 != null ? taskInfo2.topActivity : null; + writer.println(prefix + " t1: (id=" + taskInfo1.taskId + + "; package=" + (cn1 != null ? cn1.getPackageName() + ")" : "no package)") + + " t2: (id=" + (taskInfo2 != null ? taskInfo2.taskId : "-1") + + "; package=" + (cn2 != null ? cn2.getPackageName() + ")" : "no package)")); } writer.println(prefix + " ]"); } From 4a92267d1dd073b246d9f2bf201dd0040166b00a Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Thu, 27 Oct 2022 16:42:24 -0700 Subject: [PATCH 02/14] Making code more readable by removing global variable used for optimizations Using the stack was more efficient because it prevented the creation of new rectangles but it makes the code harder to read and prone to bugs if the global state of the stack of rectangles gets corrupted in any way. When this optimization was written in 2008 it was necessary but now I don't think it would have a big impact. The stack size is on average of 30 and the rectangles are only created when doing the reorder which runs about once per second if the user moves the finger too quickly. Bug: 188081026 Test: atest ReorderWidgets Change-Id: I35d8ee8d92f01035e72fe5763a7de47f4b6a73de --- src/com/android/launcher3/CellLayout.java | 25 +---------------------- 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index e66d441912..bf63ec0c0a 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -402,7 +402,6 @@ public class CellLayout extends ViewGroup { mCountY = y; mOccupied = new GridOccupancy(mCountX, mCountY); mTmpOccupied = new GridOccupancy(mCountX, mCountY); - mTempRectStack.clear(); mShortcutsAndWidgets.setCellDimensions(mCellWidth, mCellHeight, mCountX, mCountY, mBorderSpace); requestLayout(); @@ -1247,21 +1246,6 @@ public class CellLayout extends ViewGroup { result, resultSpan); } - private final Stack mTempRectStack = new Stack<>(); - private void lazyInitTempRectStack() { - if (mTempRectStack.isEmpty()) { - for (int i = 0; i < mCountX * mCountY; i++) { - mTempRectStack.push(new Rect()); - } - } - } - - private void recycleTempRects(Stack used) { - while (!used.isEmpty()) { - mTempRectStack.push(used.pop()); - } - } - /** * Find a vacant area that will fit the given bounds nearest the requested * cell location. Uses Euclidean distance to score multiple vacant areas. @@ -1281,8 +1265,6 @@ public class CellLayout extends ViewGroup { */ private int[] findNearestArea(int relativeXPos, int relativeYPos, int minSpanX, int minSpanY, int spanX, int spanY, boolean ignoreOccupied, int[] result, int[] resultSpan) { - lazyInitTempRectStack(); - // For items with a spanX / spanY > 1, the passed in point (relativeXPos, relativeYPos) // corresponds to the center of the item, but we are searching based on the top-left cell, // so we translate the point over to correspond to the top-left. @@ -1352,9 +1334,6 @@ public class CellLayout extends ViewGroup { hitMaxY |= ySize >= spanY; incX = !incX; } - incX = true; - hitMaxX = xSize >= spanX; - hitMaxY = ySize >= spanY; } final int[] cellXY = mTmpPoint; cellToCenterPoint(x, y, cellXY); @@ -1362,8 +1341,7 @@ public class CellLayout extends ViewGroup { // We verify that the current rect is not a sub-rect of any of our previous // candidates. In this case, the current rect is disqualified in favour of the // containing rect. - Rect currentRect = mTempRectStack.pop(); - currentRect.set(x, y, x + xSize, y + ySize); + Rect currentRect = new Rect(x, y, x + xSize, y + ySize); boolean contained = false; for (Rect r : validRegions) { if (r.contains(currentRect)) { @@ -1393,7 +1371,6 @@ public class CellLayout extends ViewGroup { bestXY[0] = -1; bestXY[1] = -1; } - recycleTempRects(validRegions); return bestXY; } From a793fc6e2c41a1569ad59d2ab4bc6ecb8b45db69 Mon Sep 17 00:00:00 2001 From: Holly Sun Date: Mon, 3 Oct 2022 16:56:26 -0700 Subject: [PATCH 03/14] Bring up IME and focus on input when scroll to top. Do this behind a feature flag AND only when "Always show keyboard" is selected or in prefix state. Bug: 218846025 Test: keyboard shown when swiping up for QSB and AA+ (both main and work). Keyboard not shown when either the feature flag or Always show keyboard is disabled. Change-Id: I3df2e0e44a8313eaf749cd6b91b7f9d0b9b80ec3 --- .../allapps/BaseAllAppsContainerView.java | 10 +++--- .../launcher3/allapps/BaseSearchConfig.java | 7 +++++ .../allapps/LauncherAllAppsContainerView.java | 31 +++++++++++++++++++ .../launcher3/config/FeatureFlags.java | 4 +++ 4 files changed, 48 insertions(+), 4 deletions(-) diff --git a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java index 2ff992ee9f..da86d98b5e 100644 --- a/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java +++ b/src/com/android/launcher3/allapps/BaseAllAppsContainerView.java @@ -107,8 +107,7 @@ public abstract class BaseAllAppsContainerView { + private final RecyclerView.OnScrollListener mActivityScrollListener = + new RecyclerView.OnScrollListener() { + @Override + public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { + int scrolledOffset = recyclerView.computeVerticalScrollOffset(); + ExtendedEditText input = mSearchUiManager.getEditText(); + // Scroll up and scroll to top + if (dy < 0 && scrolledOffset == 0 && input != null) { + boolean isImeEnabledOnSwipeUp = Launcher.getLauncher(mActivityContext) + .getSearchConfig().isImeEnabledOnSwipeUp(); + if (isImeEnabledOnSwipeUp || !TextUtils.isEmpty(input.getText())) { + input.showKeyboard(); + } + } + } + }; + + @Override + protected void onInitializeRecyclerView(RecyclerView rv) { + super.onInitializeRecyclerView(rv); + if (FeatureFlags.SCROLL_TOP_TO_RESET.get()) { + rv.addOnScrollListener(mActivityScrollListener); + } + } + public LauncherAllAppsContainerView(Context context) { this(context, null); } diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 037a77e44f..7913c4a0cb 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -290,6 +290,10 @@ public final class FeatureFlags { public static final BooleanFlag ENABLE_WIDGET_PICKER_DEPTH = new DeviceFlag( "ENABLE_WIDGET_PICKER_DEPTH", true, "Enable changing depth in widget picker."); + public static final BooleanFlag SCROLL_TOP_TO_RESET = new DeviceFlag( + "SCROLL_TOP_TO_RESET", false, "Bring up IME and focus on " + + "input when scroll to top if 'Always show keyboard' is enabled or in prefix state"); + public static final BooleanFlag SHOW_DELIGHTFUL_PAGINATION = getDebugFlag( "SHOW_DELIGHTFUL_PAGINATION", false, "Enable showing the new 'delightful pagination' which is a brand" From 20ce6a97fddefb2c348297c632d69e16da9e79d5 Mon Sep 17 00:00:00 2001 From: Vinit Nayak Date: Wed, 2 Nov 2022 21:56:55 -0700 Subject: [PATCH 04/14] Switch to screenshot when finishing recents animation for split select * There's a situation where we can also let the live-tile play if it wasn't the one selected for split select, but there's some bug w/ the focused task that causes the thumbnail and live tile translations to be incorrect. * But this maintains parity. Fixes: 255915066 Test: Starting/ending/ignoring live tile in split never shows the cutout Change-Id: I2f63ca44a5a1cc16df12e7b3b06ca211dc995f8d --- .../com/android/quickstep/views/RecentsView.java | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index ce96b7168e..e49fdc1d14 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -2897,6 +2897,19 @@ public abstract class RecentsView finishRecentsAnimation(true /* toRecents */, + false /* shouldPip */, null /* onFinishComplete */)); + } + } + }); anim.addEndListener(success -> { if (success) { InteractionJankMonitorWrapper.end( @@ -4187,8 +4200,6 @@ public abstract class RecentsView Date: Thu, 3 Nov 2022 11:12:30 -0700 Subject: [PATCH 05/14] Update all set page navigations settings button callback Updated the all set page navigation settings button callback to allow going back to the all set page. Fixes: 256998811 Test: going to the navigation settings then swiping home/going to another app does not allow the user to then go back to all set page. going to navigation settings then going back sends the user back to all set page Change-Id: I502e2264886c43fe15bed3fab3a241e9713c5da5 --- .../src/com/android/quickstep/interaction/AllSetActivity.java | 1 - 1 file changed, 1 deletion(-) diff --git a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java index 8ad17cb711..83dd1ad861 100644 --- a/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java +++ b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java @@ -135,7 +135,6 @@ public class AllSetActivity extends Activity { } catch (URISyntaxException e) { Log.e(LOG_TAG, "Failed to parse system nav settings intent", e); } - finish(); }); findViewById(R.id.hint).setAccessibilityDelegate(new SkipButtonAccessibilityDelegate()); From 6e78a800c1dbaa844d84910b60ebd95447c1453e Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 3 Nov 2022 12:46:56 -0700 Subject: [PATCH 06/14] Removing unused SplitScreen bounds notification from systemUI Bug: 221961069 Test: Presubmit Change-Id: I472a94dd1096a3c44cb378e647927637d9d67bf5 --- .../quickstep/TouchInteractionService.java | 31 +---- .../quickstep/util/SplitScreenBounds.java | 107 ------------------ .../android/quickstep/views/RecentsView.java | 11 +- 3 files changed, 6 insertions(+), 143 deletions(-) delete mode 100644 quickstep/src/com/android/quickstep/util/SplitScreenBounds.java diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 43aafbe8ad..b33ef25372 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -15,6 +15,7 @@ */ package com.android.quickstep; +import static android.accessibilityservice.AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS; import static android.view.MotionEvent.ACTION_CANCEL; import static android.view.MotionEvent.ACTION_DOWN; import static android.view.MotionEvent.ACTION_UP; @@ -46,7 +47,6 @@ import android.app.Service; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.graphics.Rect; import android.graphics.Region; import android.graphics.drawable.Icon; import android.os.Build; @@ -86,7 +86,6 @@ import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.OnboardingPrefs; import com.android.launcher3.util.TraceHelper; -import com.android.launcher3.util.WindowBounds; import com.android.quickstep.inputconsumers.AccessibilityInputConsumer; import com.android.quickstep.inputconsumers.AssistantInputConsumer; import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer; @@ -103,7 +102,6 @@ import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActiveGestureLog.CompoundString; import com.android.quickstep.util.ProtoTracer; import com.android.quickstep.util.ProxyScreenStatusProvider; -import com.android.quickstep.util.SplitScreenBounds; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.ISystemUiProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -142,18 +140,7 @@ public class TouchInteractionService extends Service private static final boolean BUBBLES_HOME_GESTURE_ENABLED = SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true); - private static final String KEY_BACK_NOTIFICATION_COUNT = "backNotificationCount"; - private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE"; private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once"; - private static final int MAX_BACK_NOTIFICATION_COUNT = 3; - - /** - * System Action ID to show all apps. - * TODO: Use AccessibilityService's corresponding global action constant in S - */ - private static final int SYSTEM_ACTION_ID_ALL_APPS = 14; - - private int mBackGestureNotificationCounter = -1; private final TISBinder mTISBinder = new TISBinder(); @@ -264,12 +251,6 @@ public class TouchInteractionService extends Service MAIN_EXECUTOR.execute(() -> mDeviceState.setDeferredGestureRegion(region)); } - @Override - public void onSplitScreenSecondaryBoundsChanged(Rect bounds, Rect insets) { - WindowBounds wb = new WindowBounds(bounds, insets); - MAIN_EXECUTOR.execute(() -> SplitScreenBounds.INSTANCE.setSecondaryWindowBounds(wb)); - } - @BinderThread @Override public void onScreenTurnedOn() { @@ -483,8 +464,6 @@ public class TouchInteractionService extends Service // Temporarily disable model preload // new ModelPreload().start(this); - mBackGestureNotificationCounter = Math.max(0, Utilities.getDevicePrefs(this) - .getInt(KEY_BACK_NOTIFICATION_COUNT, MAX_BACK_NOTIFICATION_COUNT)); resetHomeBounceSeenOnQuickstepEnabledFirstTime(); mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange); @@ -522,11 +501,11 @@ public class TouchInteractionService extends Service Icon.createWithResource(this, R.drawable.ic_apps), getString(R.string.all_apps_label), getString(R.string.all_apps_label), - PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent, + PendingIntent.getActivity(this, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE)); - am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS); + am.registerSystemAction(allAppsAction, GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); } else { - am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); + am.unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); } StatefulActivity newOverviewActivity = mOverviewComponentObserver.getActivityInterface() @@ -604,7 +583,7 @@ public class TouchInteractionService extends Service ProtoTracer.INSTANCE.get(this).remove(this); getSystemService(AccessibilityManager.class) - .unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS); + .unregisterSystemAction(GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS); mTaskbarManager.destroy(); sConnected = false; diff --git a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java b/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java deleted file mode 100644 index 483a1c652a..0000000000 --- a/quickstep/src/com/android/quickstep/util/SplitScreenBounds.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright (C) 2020 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.quickstep.util; - -import static android.view.Surface.ROTATION_0; -import static android.view.Surface.ROTATION_180; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.view.WindowManager; -import android.view.WindowMetrics; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.UiThread; - -import com.android.launcher3.R; -import com.android.launcher3.util.DisplayController; -import com.android.launcher3.util.WindowBounds; - -import java.util.ArrayList; - -/** - * Utility class to hold the information abound a window bounds for split screen - */ -@TargetApi(Build.VERSION_CODES.R) -public class SplitScreenBounds { - - public static final SplitScreenBounds INSTANCE = new SplitScreenBounds(); - private final ArrayList mListeners = new ArrayList<>(); - - @Nullable - private WindowBounds mBounds; - - private SplitScreenBounds() { } - - @UiThread - public void setSecondaryWindowBounds(@NonNull WindowBounds bounds) { - if (!bounds.equals(mBounds)) { - mBounds = bounds; - for (OnChangeListener listener : mListeners) { - listener.onSecondaryWindowBoundsChanged(); - } - } - } - - public @NonNull WindowBounds getSecondaryWindowBounds(Context context) { - if (mBounds == null) { - mBounds = createDefaultWindowBounds(context); - } - return mBounds; - } - - /** - * Creates window bounds as 50% of device size - */ - private static WindowBounds createDefaultWindowBounds(Context context) { - WindowMetrics wm = context.getSystemService(WindowManager.class).getMaximumWindowMetrics(); - WindowBounds bounds = WindowBounds.fromWindowMetrics(wm); - - int rotation = DisplayController.INSTANCE.get(context).getInfo().rotation; - int halfDividerSize = context.getResources() - .getDimensionPixelSize(R.dimen.multi_window_task_divider_size) / 2; - - if (rotation == ROTATION_0 || rotation == ROTATION_180) { - bounds.bounds.top = bounds.insets.top + bounds.availableSize.y / 2 + halfDividerSize; - bounds.insets.top = 0; - } else { - bounds.bounds.left = bounds.insets.left + bounds.availableSize.x / 2 + halfDividerSize; - bounds.insets.left = 0; - } - return new WindowBounds(bounds.bounds, bounds.insets); - } - - public void addOnChangeListener(OnChangeListener listener) { - mListeners.add(listener); - } - - public void removeOnChangeListener(OnChangeListener listener) { - mListeners.remove(listener); - } - - /** - * Interface to receive window bounds changes - */ - public interface OnChangeListener { - - /** - * Called when window bounds for secondary window changes - */ - void onSecondaryWindowBoundsChanged(); - } -} diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 8be544ec98..4479be8578 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -178,7 +178,6 @@ import com.android.quickstep.util.GroupTask; import com.android.quickstep.util.LayoutUtils; import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.SplitAnimationTimings; -import com.android.quickstep.util.SplitScreenBounds; import com.android.quickstep.util.SplitSelectStateController; import com.android.quickstep.util.SurfaceTransaction; import com.android.quickstep.util.SurfaceTransactionApplier; @@ -210,7 +209,7 @@ import java.util.function.Consumer; public abstract class RecentsView, STATE_TYPE extends BaseState> extends PagedView implements Insettable, TaskThumbnailCache.HighResLoadingState.HighResLoadingStateChangedCallback, - TaskVisualsChangeListener, SplitScreenBounds.OnChangeListener { + TaskVisualsChangeListener { private static final String TAG = "RecentsView"; private static final boolean DEBUG = false; @@ -948,7 +947,6 @@ public abstract class RecentsView Date: Tue, 27 Sep 2022 12:38:45 -0700 Subject: [PATCH 07/14] Update active gesture error detection - Removed some unused error detection event tracking - Updated incorrect event tracking - Added debug new debug gesture logs Bug: 254250355 Test: attempted several gestures and checked logs Change-Id: Ia1291a08a9a5842addb93c30eb6d21c6c60916c7 --- .../android/quickstep/AbsSwipeUpHandler.java | 17 +-- .../com/android/quickstep/GestureState.java | 3 - .../quickstep/RecentsAnimationCallbacks.java | 6 +- .../quickstep/RecentsAnimationController.java | 5 + .../quickstep/TaskAnimationManager.java | 9 ++ .../quickstep/TouchInteractionService.java | 16 ++- .../OtherActivityInputConsumer.java | 6 - .../ProgressDelegateInputConsumer.java | 14 +- .../util/ActiveGestureErrorDetector.java | 130 +++++++++++------- 9 files changed, 120 insertions(+), 86 deletions(-) diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java index 5cc5f10e4f..7e4c2f690a 100644 --- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java +++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java @@ -48,7 +48,7 @@ import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_CANCELE import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.CANCEL_RECENTS_ANIMATION; -import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.EXPECTING_TASK_APPEARED; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.ON_SETTLED_ON_END_TARGET; import static com.android.quickstep.util.VibratorWrapper.OVERVIEW_HAPTIC; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; @@ -1704,10 +1704,6 @@ public abstract class AbsSwipeUpHandler, private void resumeLastTask() { if (mRecentsAnimationController != null) { mRecentsAnimationController.finish(false /* toRecents */, null); - ActiveGestureLog.INSTANCE.addLog( - /* event= */ "finishRecentsAnimation", - /* extras= */ false, - /* gestureEvent= */ FINISH_RECENTS_ANIMATION); } doLogGesture(LAST_TASK, null); reset(); @@ -1916,10 +1912,6 @@ public abstract class AbsSwipeUpHandler, mRecentsAnimationController.detachNavigationBarFromApp(true); } } - ActiveGestureLog.INSTANCE.addLog( - /* event= */ "finishRecentsAnimation", - /* extras= */ true, - /* gestureEvent= */ FINISH_RECENTS_ANIMATION); } private void finishCurrentTransitionToHome() { @@ -1931,10 +1923,6 @@ public abstract class AbsSwipeUpHandler, finishRecentsControllerToHome( () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED)); } - ActiveGestureLog.INSTANCE.addLog( - /* event= */ "finishRecentsAnimation", - /* extras= */ true, - /* gestureEvent= */ FINISH_RECENTS_ANIMATION); doLogGesture(HOME, mRecentsView == null ? null : mRecentsView.getCurrentPageTaskView()); } @@ -2045,6 +2033,9 @@ public abstract class AbsSwipeUpHandler, mGestureState.updateLastStartedTaskId(taskId); boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds() .contains(taskId); + if (!hasTaskPreviouslyAppeared) { + ActiveGestureLog.INSTANCE.trackEvent(EXPECTING_TASK_APPEARED); + } nextTask.launchTask(success -> { resultCallback.accept(success); if (success) { diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java index 3c4ee75aae..31b78b3ec3 100644 --- a/quickstep/src/com/android/quickstep/GestureState.java +++ b/quickstep/src/com/android/quickstep/GestureState.java @@ -21,7 +21,6 @@ import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERV import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_HOME; -import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_LAST_TASK; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.SET_END_TARGET_NEW_TASK; import android.annotation.Nullable; @@ -341,8 +340,6 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_NEW_TASK); break; case LAST_TASK: - ActiveGestureLog.INSTANCE.trackEvent(SET_END_TARGET_LAST_TASK); - break; case RECENTS: default: // No-Op diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java index 2451ad813b..b82ff03b34 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java @@ -130,7 +130,7 @@ public class RecentsAnimationCallbacks implements public final void onAnimationCanceled(HashMap thumbnailDatas) { Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { ActiveGestureLog.INSTANCE.addLog( - /* event= */ "onRecentsAnimationCancelled", + /* event= */ "RecentsAnimationCallbacks.onAnimationCanceled", /* gestureEvent= */ CANCEL_RECENTS_ANIMATION); for (RecentsAnimationListener listener : getListeners()) { listener.onRecentsAnimationCanceled(thumbnailDatas); @@ -142,7 +142,7 @@ public class RecentsAnimationCallbacks implements @Override public void onTasksAppeared(RemoteAnimationTarget[] apps) { Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { - ActiveGestureLog.INSTANCE.addLog("onTasksAppeared", + ActiveGestureLog.INSTANCE.addLog("RecentsAnimationCallbacks.onTasksAppeared", ActiveGestureErrorDetector.GestureEvent.TASK_APPEARED); for (RecentsAnimationListener listener : getListeners()) { listener.onTasksAppeared(apps); @@ -164,6 +164,8 @@ public class RecentsAnimationCallbacks implements private final void onAnimationFinished(RecentsAnimationController controller) { Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), () -> { + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "RecentsAnimationCallbacks.onAnimationFinished"); for (RecentsAnimationListener listener : getListeners()) { listener.onRecentsAnimationFinished(controller); } diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java index 81e3782142..cf1c137707 100644 --- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java +++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java @@ -18,6 +18,7 @@ package com.android.quickstep; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.TaskAnimationManager.ENABLE_SHELL_TRANSITIONS; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FINISH_RECENTS_ANIMATION; import android.content.Context; import android.os.RemoteException; @@ -155,6 +156,10 @@ public class RecentsAnimationController { mPendingFinishCallbacks.add(callback); return; } + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "finishRecentsAnimation", + /* extras= */ toRecents, + /* gestureEvent= */ FINISH_RECENTS_ANIMATION); // Finish not yet requested mFinishRequested = true; diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java index 90e80916eb..c45b2f05f8 100644 --- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java +++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java @@ -21,6 +21,7 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION; import static com.android.systemui.shared.system.RemoteTransitionCompat.newRemoteTransition; import android.app.ActivityManager; @@ -38,6 +39,7 @@ import androidx.annotation.UiThread; import com.android.launcher3.Utilities; import com.android.launcher3.config.FeatureFlags; import com.android.quickstep.TopTaskTracker.CachedTaskInfo; +import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.views.RecentsView; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.ActivityManagerWrapper; @@ -101,6 +103,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn @UiThread public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState, Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) { + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "startRecentsAnimation", + /* gestureEvent= */ START_RECENTS_ANIMATION); // Notify if recents animation is still running if (mController != null) { String msg = "New recents animation started before old animation completed"; @@ -250,6 +255,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn * Continues the existing running recents animation for a new gesture. */ public RecentsAnimationCallbacks continueRecentsAnimation(GestureState gestureState) { + ActiveGestureLog.INSTANCE.addLog(/* event= */ "continueRecentsAnimation"); mCallbacks.removeListener(mLastGestureState); mLastGestureState = gestureState; mCallbacks.addListener(gestureState); @@ -288,6 +294,8 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn */ public void finishRunningRecentsAnimation(boolean toHome) { if (mController != null) { + ActiveGestureLog.INSTANCE.addLog( + /* event= */ "finishRunningRecentsAnimation", toHome); mCallbacks.notifyAnimationCanceled(); Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome ? mController::finishAnimationToHome @@ -320,6 +328,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn * Cleans up the recents animation entirely. */ private void cleanUpRecentsAnimation() { + ActiveGestureLog.INSTANCE.addLog(/* event= */ "cleanUpRecentsAnimation"); if (mLiveTileCleanUpHandler != null) { mLiveTileCleanUpHandler.run(); mLiveTileCleanUpHandler = null; diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 43aafbe8ad..dea666bcb3 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -22,6 +22,7 @@ import static android.view.MotionEvent.ACTION_UP; import static com.android.launcher3.config.FeatureFlags.ASSISTANT_GIVES_LAUNCHER_FOCUS; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.quickstep.GestureState.DEFAULT_STATE; +import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN; import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP; import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS; @@ -927,6 +928,9 @@ public class TouchInteractionService extends Service .append(consumer.getName()) .append(". reason(s):") .append(reasonString)); + if ((consumer.getType() & InputConsumer.TYPE_OTHER_ACTIVITY) != 0) { + ActiveGestureLog.INSTANCE.trackEvent(FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER); + } } private void handleOrientationSetup(InputConsumer baseInputConsumer) { @@ -1251,12 +1255,22 @@ public class TouchInteractionService extends Service } private void onCommand(PrintWriter pw, LinkedList args) { - switch (args.pollFirst()) { + String cmd = args.pollFirst(); + if (cmd == null) { + pw.println("Command missing"); + printAvailableCommands(pw); + return; + } + switch (cmd) { case "clear-touch-log": ActiveGestureLog.INSTANCE.clear(); break; case "print-gesture-log": ActiveGestureLog.INSTANCE.dump("", pw); + break; + default: + pw.println("Command does not exist: " + cmd); + printAvailableCommands(pw); } } diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java index 9d269fbc9e..b3d3c3dbf6 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java @@ -30,7 +30,6 @@ import static com.android.launcher3.Utilities.squaredHypot; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS; import static com.android.launcher3.util.VelocityUtils.PX_PER_MS; -import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.START_RECENTS_ANIMATION; import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID; import android.annotation.TargetApi; @@ -67,7 +66,6 @@ import com.android.quickstep.RecentsAnimationDeviceState; import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.RotationTouchHelper; import com.android.quickstep.TaskAnimationManager; -import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.CachedEventDispatcher; import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.NavBarPosition; @@ -387,10 +385,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC } private void startTouchTrackingForWindowAnimation(long touchTimeMs) { - ActiveGestureLog.INSTANCE.addLog( - /* event= */ "startRecentsAnimation", - /* gestureEvent= */ START_RECENTS_ANIMATION); - mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs); mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished); mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener()); diff --git a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java index b9b5e7c2b9..246239449f 100644 --- a/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java +++ b/quickstep/src/com/android/quickstep/inputconsumers/ProgressDelegateInputConsumer.java @@ -28,8 +28,6 @@ import android.content.Intent; import android.graphics.Point; import android.view.MotionEvent; -import androidx.annotation.Nullable; - import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; @@ -43,7 +41,6 @@ import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; import com.android.quickstep.RecentsAnimationTargets; import com.android.quickstep.TaskAnimationManager; -import com.android.quickstep.util.ActiveGestureErrorDetector; import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.system.InputMonitorCompat; @@ -102,8 +99,7 @@ public class ProgressDelegateInputConsumer implements InputConsumer, mDisplaySize = DisplayController.INSTANCE.get(context).getInfo().currentSize; // Init states - mStateCallback = new MultiStateCallback( - STATE_NAMES, ProgressDelegateInputConsumer::getTrackedEventForState); + mStateCallback = new MultiStateCallback(STATE_NAMES); mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_HANDLER_INVALIDATED, this::endRemoteAnimation); mStateCallback.runOnceAtState(STATE_TARGET_RECEIVED | STATE_FLING_FINISHED, @@ -113,14 +109,6 @@ public class ProgressDelegateInputConsumer implements InputConsumer, mSwipeDetector.setDetectableScrollConditions(DIRECTION_POSITIVE, false); } - @Nullable - private static ActiveGestureErrorDetector.GestureEvent getTrackedEventForState(int stateFlag) { - if (stateFlag == STATE_HANDLER_INVALIDATED) { - return ActiveGestureErrorDetector.GestureEvent.STATE_HANDLER_INVALIDATED; - } - return null; - } - @Override public int getType() { return TYPE_PROGRESS_DELEGATE; diff --git a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java index 53e0c2b631..60065fb16c 100644 --- a/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java +++ b/quickstep/src/com/android/quickstep/util/ActiveGestureErrorDetector.java @@ -33,10 +33,11 @@ public class ActiveGestureErrorDetector { * Enums associated to gesture navigation events. */ public enum GestureEvent { - MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_LAST_TASK, - SET_END_TARGET_NEW_TASK, ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, - FINISH_RECENTS_ANIMATION, CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, - CANCEL_CURRENT_ANIMATION, CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, + MOTION_DOWN, MOTION_UP, SET_END_TARGET, SET_END_TARGET_HOME, SET_END_TARGET_NEW_TASK, + ON_SETTLED_ON_END_TARGET, START_RECENTS_ANIMATION, FINISH_RECENTS_ANIMATION, + CANCEL_RECENTS_ANIMATION, SET_ON_PAGE_TRANSITION_END_CALLBACK, CANCEL_CURRENT_ANIMATION, + CLEANUP_SCREENSHOT, SCROLLER_ANIMATION_ABORTED, TASK_APPEARED, EXPECTING_TASK_APPEARED, + FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER, /** * These GestureEvents are specifically associated to state flags that get set in @@ -91,35 +92,40 @@ public class ActiveGestureErrorDetector { case MOTION_UP: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_DOWN), - /* errorMessage= */ prefix + "\t\tMotion up detected before/without" + prefix, + /* errorMessage= */ "Motion up detected before/without" + " motion down.", writer); break; case ON_SETTLED_ON_END_TARGET: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.SET_END_TARGET), - /* errorMessage= */ prefix + "\t\tonSettledOnEndTarget called " + prefix, + /* errorMessage= */ "onSettledOnEndTarget called " + "before/without setEndTarget.", writer); break; case FINISH_RECENTS_ANIMATION: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION), - /* errorMessage= */ prefix + "\t\tfinishRecentsAnimation called " + prefix, + /* errorMessage= */ "finishRecentsAnimation called " + "before/without startRecentsAnimation.", writer); break; case CANCEL_RECENTS_ANIMATION: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.START_RECENTS_ANIMATION), - /* errorMessage= */ prefix + "\t\tcancelRecentsAnimation called " + prefix, + /* errorMessage= */ "cancelRecentsAnimation called " + "before/without startRecentsAnimation.", writer); break; case CLEANUP_SCREENSHOT: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED), - /* errorMessage= */ prefix + "\t\trecents activity screenshot was " + prefix, + /* errorMessage= */ "recents activity screenshot was " + "cleaned up before/without STATE_SCREENSHOT_CAPTURED " + "being set.", writer); @@ -129,48 +135,66 @@ public class ActiveGestureErrorDetector { encounteredEvents.contains(GestureEvent.SET_END_TARGET_HOME) && !encounteredEvents.contains( GestureEvent.ON_SETTLED_ON_END_TARGET), - /* errorMessage= */ prefix + "\t\trecents view scroller animation " + prefix, + /* errorMessage= */ "recents view scroller animation " + "aborted after setting end target HOME, but before" + " settling on end target.", writer); break; case TASK_APPEARED: errorDetected |= printErrorIfTrue( - !encounteredEvents.contains(GestureEvent.SET_END_TARGET_LAST_TASK) - && !encounteredEvents.contains( - GestureEvent.SET_END_TARGET_NEW_TASK), - /* errorMessage= */ prefix + "\t\tonTasksAppeared called " - + "before/without setting end target to last or new task", + !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK), + prefix, + /* errorMessage= */ "onTasksAppeared called " + + "before/without setting end target to new task", + writer); + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.EXPECTING_TASK_APPEARED), + prefix, + /* errorMessage= */ "onTasksAppeared was not expected to be called", + writer); + break; + case EXPECTING_TASK_APPEARED: + errorDetected |= printErrorIfTrue( + !encounteredEvents.contains(GestureEvent.SET_END_TARGET_NEW_TASK), + prefix, + /* errorMessage= */ "expecting onTasksAppeared to be called " + + "before/without setting end target to new task", writer); break; case STATE_GESTURE_COMPLETED: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_UP), - /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set " + prefix, + /* errorMessage= */ "STATE_GESTURE_COMPLETED set " + "before/without motion up.", writer); errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED), - /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_COMPLETED set " + prefix, + /* errorMessage= */ "STATE_GESTURE_COMPLETED set " + "before/without STATE_GESTURE_STARTED.", writer); break; case STATE_GESTURE_CANCELLED: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_UP), - /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set " + prefix, + /* errorMessage= */ "STATE_GESTURE_CANCELLED set " + "before/without motion up.", writer); errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED), - /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_CANCELLED set " + prefix, + /* errorMessage= */ "STATE_GESTURE_CANCELLED set " + "before/without STATE_GESTURE_STARTED.", writer); break; case STATE_SCREENSHOT_CAPTURED: errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.STATE_CAPTURE_SCREENSHOT), - /* errorMessage= */ prefix + "\t\tSTATE_SCREENSHOT_CAPTURED set " + prefix, + /* errorMessage= */ "STATE_SCREENSHOT_CAPTURED set " + "before/without STATE_CAPTURE_SCREENSHOT.", writer); break; @@ -178,7 +202,8 @@ public class ActiveGestureErrorDetector { errorDetected |= printErrorIfTrue( !encounteredEvents.contains( GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK), - /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_SCROLLING_FINISHED " + prefix, + /* errorMessage= */ "STATE_RECENTS_SCROLLING_FINISHED " + "set before/without calling " + "setOnPageTransitionEndCallback.", writer); @@ -187,16 +212,19 @@ public class ActiveGestureErrorDetector { errorDetected |= printErrorIfTrue( !encounteredEvents.contains( GestureEvent.START_RECENTS_ANIMATION), - /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED " + prefix, + /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED " + "set before/without startRecentsAnimation.", writer); break; case MOTION_DOWN: case SET_END_TARGET: case SET_END_TARGET_HOME: + case SET_END_TARGET_NEW_TASK: case START_RECENTS_ANIMATION: case SET_ON_PAGE_TRANSITION_END_CALLBACK: case CANCEL_CURRENT_ANIMATION: + case FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER: case STATE_GESTURE_STARTED: case STATE_END_TARGET_ANIMATION_FINISHED: case STATE_CAPTURE_SCREENSHOT: @@ -210,31 +238,36 @@ public class ActiveGestureErrorDetector { // Check that all required events were found. errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_DOWN), - /* errorMessage= */ prefix + "\t\tMotion down never detected.", + prefix, + /* errorMessage= */ "Motion down never detected.", writer); errorDetected |= printErrorIfTrue( !encounteredEvents.contains(GestureEvent.MOTION_UP), - /* errorMessage= */ prefix + "\t\tMotion up never detected.", + prefix, + /* errorMessage= */ "Motion up never detected.", writer); errorDetected |= printErrorIfTrue( /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET) && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET), - /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but " + prefix, + /* errorMessage= */ "setEndTarget was called, but " + "onSettledOnEndTarget wasn't.", writer); errorDetected |= printErrorIfTrue( /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET) && !encounteredEvents.contains( GestureEvent.STATE_END_TARGET_ANIMATION_FINISHED), - /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but " + prefix, + /* errorMessage= */ "setEndTarget was called, but " + "STATE_END_TARGET_ANIMATION_FINISHED was never set.", writer); errorDetected |= printErrorIfTrue( /* condition= */ encounteredEvents.contains(GestureEvent.SET_END_TARGET) && !encounteredEvents.contains( GestureEvent.STATE_RECENTS_SCROLLING_FINISHED), - /* errorMessage= */ prefix + "\t\tsetEndTarget was called, but " + prefix, + /* errorMessage= */ "setEndTarget was called, but " + "STATE_RECENTS_SCROLLING_FINISHED was never set.", writer); errorDetected |= printErrorIfTrue( @@ -243,7 +276,8 @@ public class ActiveGestureErrorDetector { && encounteredEvents.contains( GestureEvent.STATE_RECENTS_SCROLLING_FINISHED) && !encounteredEvents.contains(GestureEvent.ON_SETTLED_ON_END_TARGET), - /* errorMessage= */ prefix + "\t\tSTATE_END_TARGET_ANIMATION_FINISHED and " + prefix, + /* errorMessage= */ "STATE_END_TARGET_ANIMATION_FINISHED and " + "STATE_RECENTS_SCROLLING_FINISHED were set, but onSettledOnEndTarget " + "wasn't called.", writer); @@ -253,7 +287,8 @@ public class ActiveGestureErrorDetector { GestureEvent.START_RECENTS_ANIMATION) && !encounteredEvents.contains(GestureEvent.FINISH_RECENTS_ANIMATION) && !encounteredEvents.contains(GestureEvent.CANCEL_RECENTS_ANIMATION), - /* errorMessage= */ prefix + "\t\tstartRecentsAnimation was called, but " + prefix, + /* errorMessage= */ "startRecentsAnimation was called, but " + "finishRecentsAnimation and cancelRecentsAnimation weren't.", writer); @@ -261,7 +296,8 @@ public class ActiveGestureErrorDetector { /* condition= */ encounteredEvents.contains(GestureEvent.STATE_GESTURE_STARTED) && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_COMPLETED) && !encounteredEvents.contains(GestureEvent.STATE_GESTURE_CANCELLED), - /* errorMessage= */ prefix + "\t\tSTATE_GESTURE_STARTED was set, but " + prefix, + /* errorMessage= */ "STATE_GESTURE_STARTED was set, but " + "STATE_GESTURE_COMPLETED and STATE_GESTURE_CANCELLED weren't.", writer); @@ -269,7 +305,8 @@ public class ActiveGestureErrorDetector { /* condition= */ encounteredEvents.contains( GestureEvent.STATE_CAPTURE_SCREENSHOT) && !encounteredEvents.contains(GestureEvent.STATE_SCREENSHOT_CAPTURED), - /* errorMessage= */ prefix + "\t\tSTATE_CAPTURE_SCREENSHOT was set, but " + prefix, + /* errorMessage= */ "STATE_CAPTURE_SCREENSHOT was set, but " + "STATE_SCREENSHOT_CAPTURED wasn't.", writer); @@ -278,15 +315,18 @@ public class ActiveGestureErrorDetector { GestureEvent.SET_ON_PAGE_TRANSITION_END_CALLBACK) && !encounteredEvents.contains( GestureEvent.STATE_RECENTS_SCROLLING_FINISHED), - /* errorMessage= */ prefix + "\t\tsetOnPageTransitionEndCallback called, but " + prefix, + /* errorMessage= */ "setOnPageTransitionEndCallback called, but " + "STATE_RECENTS_SCROLLING_FINISHED wasn't set.", writer); errorDetected |= printErrorIfTrue( - /* condition= */ !encounteredEvents.contains( - GestureEvent.CANCEL_CURRENT_ANIMATION) + /* condition= */ encounteredEvents.contains( + GestureEvent.FLAG_USING_OTHER_ACTIVITY_INPUT_CONSUMER) + && !encounteredEvents.contains(GestureEvent.CANCEL_CURRENT_ANIMATION) && !encounteredEvents.contains(GestureEvent.STATE_HANDLER_INVALIDATED), - /* errorMessage= */ prefix + "\t\tAbsSwipeUpHandler.cancelCurrentAnimation " + prefix, + /* errorMessage= */ "AbsSwipeUpHandler.cancelCurrentAnimation " + "wasn't called and STATE_HANDLER_INVALIDATED wasn't set.", writer); @@ -294,23 +334,17 @@ public class ActiveGestureErrorDetector { /* condition= */ encounteredEvents.contains( GestureEvent.STATE_RECENTS_ANIMATION_CANCELED) && !encounteredEvents.contains(GestureEvent.CLEANUP_SCREENSHOT), - /* errorMessage= */ prefix + "\t\tSTATE_RECENTS_ANIMATION_CANCELED was set but " + prefix, + /* errorMessage= */ "STATE_RECENTS_ANIMATION_CANCELED was set but " + "the task screenshot wasn't cleaned up.", writer); errorDetected |= printErrorIfTrue( /* condition= */ encounteredEvents.contains( - GestureEvent.SET_END_TARGET_LAST_TASK) + GestureEvent.EXPECTING_TASK_APPEARED) && !encounteredEvents.contains(GestureEvent.TASK_APPEARED), - /* errorMessage= */ prefix + "\t\tend target set to last task, but " - + "onTaskAppeared wasn't called.", - writer); - errorDetected |= printErrorIfTrue( - /* condition= */ encounteredEvents.contains( - GestureEvent.SET_END_TARGET_NEW_TASK) - && !encounteredEvents.contains(GestureEvent.TASK_APPEARED), - /* errorMessage= */ prefix + "\t\tend target set to new task, but " - + "onTaskAppeared wasn't called.", + prefix, + /* errorMessage= */ "onTaskAppeared was expected to be called but wasn't.", writer); if (!errorDetected) { @@ -320,11 +354,11 @@ public class ActiveGestureErrorDetector { } private static boolean printErrorIfTrue( - boolean condition, String errorMessage, PrintWriter writer) { + boolean condition, String prefix, String errorMessage, PrintWriter writer) { if (!condition) { return false; } - writer.println(errorMessage); + writer.println(prefix + "\t\t- " + errorMessage); return true; } } From 50097ef272fd1e0c6f564aac34b8185da4f93ecd Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 3 Nov 2022 10:34:56 -0700 Subject: [PATCH 08/14] Waiting for app install to finish before procedding with the test Bug: 256659409 Test: Presubmit Change-Id: Ia0f4cdd072c4c439d09070b0395fcfd6909c2a8f --- .../testing/DebugTestInformationHandler.java | 4 + .../testing/TestInformationHandler.java | 32 ++++--- .../testing/shared/TestProtocol.java | 1 + tests/AndroidManifest-common.xml | 1 + .../launcher3/ui/TaplTestsLauncher3.java | 20 +++-- .../com/android/launcher3/util/TestUtil.java | 83 ++++++++++++++++--- .../tapl/LauncherInstrumentation.java | 4 + 7 files changed, 115 insertions(+), 30 deletions(-) diff --git a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java index a91ff44c8a..bdac88a043 100644 --- a/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java +++ b/ext_tests/src/com/android/launcher3/testing/DebugTestInformationHandler.java @@ -17,6 +17,7 @@ package com.android.launcher3.testing; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; +import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import android.app.Activity; import android.app.Application; @@ -248,6 +249,9 @@ public class DebugTestInformationHandler extends TestInformationHandler { return response; } + case TestProtocol.REQUEST_MODEL_QUEUE_CLEARED: + return getFromExecutorSync(MODEL_EXECUTOR, Bundle::new); + default: return super.call(method, arg, extras); } diff --git a/src/com/android/launcher3/testing/TestInformationHandler.java b/src/com/android/launcher3/testing/TestInformationHandler.java index fdd30e185b..acb7eb38a2 100644 --- a/src/com/android/launcher3/testing/TestInformationHandler.java +++ b/src/com/android/launcher3/testing/TestInformationHandler.java @@ -47,7 +47,9 @@ import com.android.launcher3.testing.shared.WorkspaceCellCenterRequest; import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.widget.picker.WidgetsFullSheet; +import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; import java.util.function.Function; import java.util.function.Supplier; @@ -214,8 +216,7 @@ public class TestInformationHandler implements ResourceBasedOverride { } case TestProtocol.REQUEST_HAS_TIS: { - response.putBoolean( - TestProtocol.REQUEST_HAS_TIS, false); + response.putBoolean(TestProtocol.REQUEST_HAS_TIS, false); return response; } @@ -266,17 +267,24 @@ public class TestInformationHandler implements ResourceBasedOverride { */ private static Bundle getUIProperty( BundleSetter bundleSetter, Function provider, Supplier targetSupplier) { + return getFromExecutorSync(MAIN_EXECUTOR, () -> { + S target = targetSupplier.get(); + if (target == null) { + return null; + } + T value = provider.apply(target); + Bundle response = new Bundle(); + bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value); + return response; + }); + } + + /** + * Executes the callback on the executor and waits for the result + */ + protected static T getFromExecutorSync(ExecutorService executor, Callable callback) { try { - return MAIN_EXECUTOR.submit(() -> { - S target = targetSupplier.get(); - if (target == null) { - return null; - } - T value = provider.apply(target); - Bundle response = new Bundle(); - bundleSetter.set(response, TestProtocol.TEST_INFO_RESPONSE_FIELD, value); - return response; - }).get(); + return executor.submit(callback).get(); } catch (ExecutionException | InterruptedException e) { throw new RuntimeException(e); } diff --git a/src/com/android/launcher3/testing/shared/TestProtocol.java b/src/com/android/launcher3/testing/shared/TestProtocol.java index 792d4757cb..3fbce888f1 100644 --- a/src/com/android/launcher3/testing/shared/TestProtocol.java +++ b/src/com/android/launcher3/testing/shared/TestProtocol.java @@ -128,6 +128,7 @@ public final class TestProtocol { public static final String REQUEST_GET_OVERVIEW_PAGE_SPACING = "get-overview-page-spacing"; public static final String REQUEST_ENABLE_ROTATION = "enable_rotation"; public static final String REQUEST_ENABLE_SUGGESTION = "enable-suggestion"; + public static final String REQUEST_MODEL_QUEUE_CLEARED = "model-queue-cleared"; public static boolean sDebugTracing = false; public static final String REQUEST_ENABLE_DEBUG_TRACING = "enable-debug-tracing"; diff --git a/tests/AndroidManifest-common.xml b/tests/AndroidManifest-common.xml index 4af8468765..bedf2772ba 100644 --- a/tests/AndroidManifest-common.xml +++ b/tests/AndroidManifest-common.xml @@ -24,6 +24,7 @@ + diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java index 01e6ed760f..978e84c393 100644 --- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java +++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java @@ -29,7 +29,6 @@ import static org.junit.Assume.assumeTrue; import android.content.Intent; import android.graphics.Point; -import android.os.SystemClock; import android.platform.test.annotations.IwTest; import androidx.test.filters.LargeTest; @@ -479,7 +478,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @Test @PortraitLandscape public void testUninstallFromWorkspace() throws Exception { - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { verifyAppUninstalledFromAllApps( createShortcutInCenterIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME); @@ -492,10 +491,8 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { @PortraitLandscape @ScreenRecord // (b/256659409) public void testUninstallFromAllApps() throws Exception { - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { - // b/256659409 - SystemClock.sleep(5000); Workspace workspace = mLauncher.getWorkspace(); final HomeAllApps allApps = workspace.switchToAllApps(); allApps.freeze(); @@ -539,7 +536,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { Point[] gridPositions = getCornersAndCenterPositions(); createShortcutIfNotExist(STORE_APP_NAME, gridPositions[0]); createShortcutIfNotExist(MAPS_APP_NAME, gridPositions[1]); - TestUtil.installDummyApp(); + installDummyAppAndWaitForUIUpdate(); try { createShortcutIfNotExist(DUMMY_APP_NAME, gridPositions[2]); Map initialPositions = @@ -590,6 +587,17 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest { mLauncher.getWorkspace().getHotseatAppIcon(APP_NAME)); } + private void installDummyAppAndWaitForUIUpdate() throws IOException { + TestUtil.installDummyApp(); + // Wait for model thread completion as it may be processing + // the install event from the SystemService + mLauncher.waitForModelQueueCleared(); + // Wait for Launcher UI thread completion, as it may be processing updating the UI in + // response to the model update. Not that `waitForLauncherInitialized` is just a proxy + // method, we can use any method which touches Launcher UI thread, + mLauncher.waitForLauncherInitialized(); + } + /** * @return List of workspace grid coordinates. Those are not pixels. See {@link * Workspace#getIconGridDimensions()} diff --git a/tests/src/com/android/launcher3/util/TestUtil.java b/tests/src/com/android/launcher3/util/TestUtil.java index 67f3902095..d7c6c4fda9 100644 --- a/tests/src/com/android/launcher3/util/TestUtil.java +++ b/tests/src/com/android/launcher3/util/TestUtil.java @@ -17,8 +17,13 @@ package com.android.launcher3.util; import static androidx.test.InstrumentationRegistry.getContext; import static androidx.test.InstrumentationRegistry.getInstrumentation; +import static androidx.test.InstrumentationRegistry.getTargetContext; +import android.content.pm.LauncherApps; import android.content.res.Resources; +import android.os.Handler; +import android.os.Looper; +import android.os.UserHandle; import androidx.test.uiautomator.UiDevice; @@ -27,6 +32,7 @@ import org.junit.Assert; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.concurrent.CountDownLatch; public class TestUtil { public static final String DUMMY_PACKAGE = "com.example.android.aardwolf"; @@ -40,24 +46,77 @@ public class TestUtil { final String apkFilename = getInstrumentation().getTargetContext(). getFilesDir().getPath() + "/dummy_app.apk"; - final FileOutputStream out = new FileOutputStream(apkFilename); - byte[] buff = new byte[1024]; - int read; + try (PackageInstallCheck pic = new PackageInstallCheck()) { + final FileOutputStream out = new FileOutputStream(apkFilename); + byte[] buff = new byte[1024]; + int read; - while ((read = in.read(buff)) > 0) { - out.write(buff, 0, read); + while ((read = in.read(buff)) > 0) { + out.write(buff, 0, read); + } + in.close(); + out.close(); + + final String result = UiDevice.getInstance(getInstrumentation()) + .executeShellCommand("pm install " + apkFilename); + Assert.assertTrue( + "Failed to install wellbeing test apk; make sure the device is rooted", + "Success".equals(result.replaceAll("\\s+", ""))); + pic.mAddWait.await(); + } catch (InterruptedException e) { + throw new IOException(e); } - in.close(); - out.close(); - - final String result = UiDevice.getInstance(getInstrumentation()) - .executeShellCommand("pm install " + apkFilename); - Assert.assertTrue("Failed to install wellbeing test apk; make sure the device is rooted", - "Success".equals(result.replaceAll("\\s+", ""))); } public static void uninstallDummyApp() throws IOException { UiDevice.getInstance(getInstrumentation()).executeShellCommand( "pm uninstall " + DUMMY_PACKAGE); } + + private static class PackageInstallCheck extends LauncherApps.Callback + implements AutoCloseable { + + final CountDownLatch mAddWait = new CountDownLatch(1); + final LauncherApps mLauncherApps; + + PackageInstallCheck() { + mLauncherApps = getTargetContext().getSystemService(LauncherApps.class); + mLauncherApps.registerCallback(this, new Handler(Looper.getMainLooper())); + } + + private void verifyPackage(String packageName) { + if (DUMMY_PACKAGE.equals(packageName)) { + mAddWait.countDown(); + } + } + + @Override + public void onPackageAdded(String packageName, UserHandle user) { + verifyPackage(packageName); + } + + @Override + public void onPackageChanged(String packageName, UserHandle user) { + verifyPackage(packageName); + } + + @Override + public void onPackageRemoved(String packageName, UserHandle user) { } + + @Override + public void onPackagesAvailable(String[] packageNames, UserHandle user, boolean replacing) { + for (String packageName : packageNames) { + verifyPackage(packageName); + } + } + + @Override + public void onPackagesUnavailable(String[] packageNames, UserHandle user, + boolean replacing) { } + + @Override + public void close() { + mLauncherApps.unregisterCallback(this); + } + } } diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java index 449b7b7631..1c5c5fa812 100644 --- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java +++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java @@ -848,6 +848,10 @@ public final class LauncherInstrumentation { } } + public void waitForModelQueueCleared() { + getTestInfo(TestProtocol.REQUEST_MODEL_QUEUE_CLEARED); + } + public void waitForLauncherInitialized() { for (int i = 0; i < 100; ++i) { if (getTestInfo( From 774dcd06aea583f6f461ee3a68e65c90dc1087ee Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 26 Oct 2022 10:29:46 -0700 Subject: [PATCH 09/14] Relying on the controller state instead of animator state for icon alignment Bug: 246644619 Test: Presubmit Change-Id: Iaa4ddd94a6d85acb9f8501263665ea78394815de --- .../taskbar/LauncherTaskbarUIController.java | 5 +++ .../TaskbarLauncherStateController.java | 33 +++++++++++-------- .../taskbar/TaskbarUIController.java | 7 ++++ .../taskbar/TaskbarViewController.java | 10 +++--- .../com/android/quickstep/AnimatedFloat.java | 20 ----------- 5 files changed, 35 insertions(+), 40 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java index c9e42b7bac..317f6a4884 100644 --- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java @@ -355,6 +355,11 @@ public class LauncherTaskbarUIController extends TaskbarUIController { mTaskbarLauncherStateController.applyState(); } + @Override + public boolean isIconAlignedWithHotseat() { + return mTaskbarLauncherStateController.isIconAlignedWithHotseat(); + } + @Override public void dumpLogs(String prefix, PrintWriter pw) { super.dumpLogs(prefix, pw); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java index bc5bcf5b28..723e21400e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarLauncherStateController.java @@ -38,9 +38,8 @@ import com.android.launcher3.Utilities; import com.android.launcher3.anim.AnimatorListeners; import com.android.launcher3.statemanager.StateManager; import com.android.launcher3.uioverrides.QuickstepLauncher; -import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.launcher3.uioverrides.states.OverviewState; -import com.android.launcher3.util.MultiValueAlpha; +import com.android.launcher3.util.MultiPropertyFactory.MultiProperty; import com.android.quickstep.AnimatedFloat; import com.android.quickstep.RecentsAnimationCallbacks; import com.android.quickstep.RecentsAnimationController; @@ -251,17 +250,7 @@ import java.util.StringJoiner; private Animator onStateChangeApplied(int changedFlags, long duration, boolean start) { boolean goingToLauncher = isInLauncher(); - final float toAlignment; - if (goingToLauncher) { - boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher); - boolean willStashVisually = isInStashedState - && mControllers.taskbarStashController.supportsVisualStashing(); - boolean isTaskbarAlignedWithHotseat = - mLauncherState.isTaskbarAlignedWithHotseat(mLauncher); - toAlignment = isTaskbarAlignedWithHotseat && !willStashVisually ? 1 : 0; - } else { - toAlignment = 0; - } + final float toAlignment = isIconAlignedWithHotseat() ? 1 : 0; if (DEBUG) { Log.d(TAG, "onStateChangeApplied - mState: " + getStateString(mState) + ", changedFlags: " + getStateString(changedFlags) @@ -357,6 +346,22 @@ import java.util.StringJoiner; return mLauncherState.isTaskbarAlignedWithHotseat(mLauncher); } + /** + * Returns if icons should be aligned to hotseat in the current transition + */ + public boolean isIconAlignedWithHotseat() { + if (isInLauncher()) { + boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher); + boolean willStashVisually = isInStashedState + && mControllers.taskbarStashController.supportsVisualStashing(); + boolean isTaskbarAlignedWithHotseat = + mLauncherState.isTaskbarAlignedWithHotseat(mLauncher); + return isTaskbarAlignedWithHotseat && !willStashVisually; + } else { + return false; + } + } + private void playStateTransitionAnim(AnimatorSet animatorSet, long duration, boolean committed) { boolean isInStashedState = mLauncherState.isTaskbarStashed(mLauncher); @@ -395,7 +400,7 @@ import java.util.StringJoiner; || (!taskbarWillBeVisible && Float.compare(currentValue, 0) != 0); mControllers.taskbarViewController.setLauncherIconAlignment( - mIconAlignment.value, mIconAlignment.getEndValue(), mLauncher.getDeviceProfile()); + mIconAlignment.value, mLauncher.getDeviceProfile()); mControllers.navbarButtonsViewController.updateTaskbarAlignment(mIconAlignment.value); // Switch taskbar and hotseat in last frame updateIconAlphaForHome(taskbarWillBeVisible ? 1 : 0); diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java index 2294306ac3..11521260e9 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java @@ -114,6 +114,13 @@ public class TaskbarUIController { || mControllers.navbarButtonsViewController.isEventOverAnyItem(ev); } + /** + * Returns true if icons should be aligned to hotseat in the current transition. + */ + public boolean isIconAlignedWithHotseat() { + return false; + } + @CallSuper protected void dumpLogs(String prefix, PrintWriter pw) { pw.println(String.format( diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java index ee87185f95..a73528b5ef 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java @@ -280,10 +280,9 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar * 0 => not aligned * 1 => fully aligned */ - public void setLauncherIconAlignment(float alignmentRatio, Float endAlignment, - DeviceProfile launcherDp) { + public void setLauncherIconAlignment(float alignmentRatio, DeviceProfile launcherDp) { if (mIconAlignControllerLazy == null) { - mIconAlignControllerLazy = createIconAlignmentController(launcherDp, endAlignment); + mIconAlignControllerLazy = createIconAlignmentController(launcherDp); } mIconAlignControllerLazy.setPlayFraction(alignmentRatio); if (alignmentRatio <= 0 || alignmentRatio >= 1) { @@ -295,8 +294,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar /** * Creates an animation for aligning the taskbar icons with the provided Launcher device profile */ - private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp, - Float endAlignment) { + private AnimatorPlaybackController createIconAlignmentController(DeviceProfile launcherDp) { mOnControllerPreCreateCallback.run(); PendingAnimation setter = new PendingAnimation(100); DeviceProfile taskbarDp = mActivity.getDeviceProfile(); @@ -322,7 +320,7 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar setter.addOnFrameListener(anim -> mActivity.setTaskbarWindowHeight( anim.getAnimatedFraction() > 0 ? expandedHeight : collapsedHeight)); - boolean isToHome = endAlignment != null && endAlignment == 1; + boolean isToHome = mControllers.uiController.isIconAlignedWithHotseat(); for (int i = 0; i < mTaskbarView.getChildCount(); i++) { View child = mTaskbarView.getChildAt(i); int positionInHotseat; diff --git a/quickstep/src/com/android/quickstep/AnimatedFloat.java b/quickstep/src/com/android/quickstep/AnimatedFloat.java index b06b894c6e..5ab3c58e4d 100644 --- a/quickstep/src/com/android/quickstep/AnimatedFloat.java +++ b/quickstep/src/com/android/quickstep/AnimatedFloat.java @@ -98,15 +98,6 @@ public class AnimatedFloat { } } - /** - * Starts the animation. - */ - public void startAnimation() { - if (mValueAnimator != null) { - mValueAnimator.start(); - } - } - public void cancelAnimation() { if (mValueAnimator != null) { mValueAnimator.cancel(); @@ -119,10 +110,6 @@ public class AnimatedFloat { } } - public ObjectAnimator getCurrentAnimation() { - return mValueAnimator; - } - public boolean isAnimating() { return mValueAnimator != null; } @@ -140,11 +127,4 @@ public class AnimatedFloat { public boolean isSettledOnValue(float endValue) { return !isAnimating() && value == endValue; } - - /** - * Returns the value we are animating to, or {@code null} if we are not currently animating. - */ - public Float getEndValue() { - return mEndValue; - } } From 5d990eedfd7013c9e3eb8cb6599b66d2c3053be3 Mon Sep 17 00:00:00 2001 From: Sebastian Franco Date: Tue, 1 Nov 2022 16:08:24 -0700 Subject: [PATCH 10/14] Ensure that the reorder preview and what gets submited uses the same solution. Design doc: https://docs.google.com/document/d/1RsId9OFGgkcImkkmDwWZBBvITWnGlxKVqniE3cimkqY/edit#heading=h.xzptrog8pyxf As can be seen in the diagram in b/256770363 on every reorder the first call in the process is MODE_SHOW_REORDER_HINT so if we make sure that only then a solution is calculated and we reuse that solution in the rest of the calls in the same reorder then we would have more consistency and the animation will always be the same and the logic state. Fix: 256770363 Bug: 188081026 Test: atest ReorderWidgets Change-Id: I93a1d09f5b8cbfbc461043ee3fc41b1cab294fed --- src/com/android/launcher3/CellLayout.java | 20 ++++++++++---------- src/com/android/launcher3/Workspace.java | 19 ++++++++----------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index 9d059bbb0e..455a2fe970 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -2543,21 +2543,21 @@ public class CellLayout extends ViewGroup { if (result == null) { result = new int[]{-1, -1}; } - ItemConfiguration finalSolution; - // When we are checking drop validity or actually dropping, we don't recompute the - // direction vector, since we want the solution to match the preview, and it's possible - // that the exact position of the item has changed to result in a new reordering outcome. - if ((mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL || mode == MODE_ACCEPT_DROP) - && mPreviousSolution != null) { + + ItemConfiguration finalSolution = null; + // We want the solution to match the animation of the preview and to match the drop so we + // only recalculate in mode MODE_SHOW_REORDER_HINT because that the first one to run in the + // reorder cycle. + if (mode == MODE_SHOW_REORDER_HINT || mPreviousSolution == null) { + finalSolution = calculateReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, + dragView); + mPreviousSolution = finalSolution; + } else { finalSolution = mPreviousSolution; // We reset this vector after drop if (mode == MODE_ON_DROP || mode == MODE_ON_DROP_EXTERNAL) { mPreviousSolution = null; } - } else { - finalSolution = calculateReorder(pixelX, pixelY, minSpanX, minSpanY, spanX, spanY, - dragView); - mPreviousSolution = finalSolution; } if (finalSolution == null || !finalSolution.isSolution) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b6eb58973d..b66f42e442 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -2467,23 +2467,20 @@ public class Workspace extends PagedView final View child = (mDragInfo == null) ? null : mDragInfo.cell; int reorderX = mTargetCell[0]; int reorderY = mTargetCell[1]; - if (!nearestDropOccupied) { - mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], - (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY, - child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT); - mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1], - item.spanX, item.spanY, d); - } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) - && !mReorderAlarm.alarmPending() + if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) && (mLastReorderX != reorderX || mLastReorderY != reorderY) && targetCellDistance < mDragTargetLayout.getReorderRadius(mTargetCell, item.spanX, item.spanY)) { - - int[] resultSpan = new int[2]; mDragTargetLayout.performReorder((int) mDragViewVisualCenter[0], (int) mDragViewVisualCenter[1], minSpanX, minSpanY, item.spanX, item.spanY, - child, mTargetCell, resultSpan, CellLayout.MODE_SHOW_REORDER_HINT); + child, mTargetCell, new int[2], CellLayout.MODE_SHOW_REORDER_HINT); + } + if (!nearestDropOccupied) { + mDragTargetLayout.visualizeDropLocation(mTargetCell[0], mTargetCell[1], + item.spanX, item.spanY, d); + } else if ((mDragMode == DRAG_MODE_NONE || mDragMode == DRAG_MODE_REORDER) + && !mReorderAlarm.alarmPending()) { // Otherwise, if we aren't adding to or creating a folder and there's no pending // reorder, then we schedule a reorder ReorderAlarmListener listener = new ReorderAlarmListener(mDragViewVisualCenter, From 82379187ea2917be75d16b1c6d0eae00ec5b86f8 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 2 Nov 2022 18:58:49 -0700 Subject: [PATCH 11/14] Adding support for multiple overlay callbacks Bug: 193244407 Test: Presubmit Change-Id: Ic345972056752238e7e46226533fd8f33d664213 --- .../uioverrides/QuickstepLauncher.java | 13 +- src/com/android/launcher3/Launcher.java | 37 ++--- src/com/android/launcher3/Workspace.java | 153 ++++-------------- .../launcher3/dragndrop/DragLayer.java | 21 ++- .../plugins/shared/LauncherExterns.java | 6 - .../shared/LauncherOverlayManager.java | 2 +- 6 files changed, 71 insertions(+), 161 deletions(-) diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index b228fdb1cf..9813c8a987 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -219,6 +219,8 @@ public class QuickstepLauncher extends Launcher { mEnableWidgetDepth = ENABLE_WIDGET_PICKER_DEPTH.get() && SystemProperties.getBoolean("ro.launcher.depth.widget", true); + getWorkspace().addOverlayCallback(progress -> + onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX)); } @Override @@ -594,17 +596,6 @@ public class QuickstepLauncher extends Launcher { recentsView.finishRecentsAnimation(true /* toRecents */, null); } - /** - * {@code LauncherOverlayCallbacks} scroll amount. - * Indicates transition progress to -1 screen. - * @param progress From 0 to 1. - */ - @Override - public void onScrollChanged(float progress) { - super.onScrollChanged(progress); - onTaskbarInAppDisplayProgressUpdate(progress, MINUS_ONE_PAGE_PROGRESS_INDEX); - } - @Override public void onAllAppsTransition(float progress) { super.onAllAppsTransition(progress); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 9426ae9a92..c73e07794f 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -53,6 +53,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_EXIT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONRESUME; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ONSTOP; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPELEFT; +import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGET_RECONFIGURED; import static com.android.launcher3.model.ItemInstallQueue.FLAG_ACTIVITY_PAUSED; import static com.android.launcher3.model.ItemInstallQueue.FLAG_DRAG_AND_DROP; @@ -183,7 +185,6 @@ import com.android.launcher3.states.RotationHelper; import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.touch.AllAppsSwipeController; -import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper; import com.android.launcher3.util.ActivityResultInfo; import com.android.launcher3.util.ActivityTracker; @@ -222,7 +223,6 @@ import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.shared.LauncherExterns; import com.android.systemui.plugins.shared.LauncherOverlayManager; import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; -import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -242,7 +242,7 @@ import java.util.stream.Stream; */ public class Launcher extends StatefulActivity implements LauncherExterns, Callbacks, InvariantDeviceProfile.OnIDPChangeListener, - PluginListener, LauncherOverlayCallbacks { + PluginListener { public static final String TAG = "Launcher"; public static final ActivityTracker ACTIVITY_TRACKER = new ActivityTracker<>(); @@ -696,17 +696,9 @@ public class Launcher extends StatefulActivity */ @Override public void setLauncherOverlay(LauncherOverlay overlay) { - if (overlay != null) { - overlay.setOverlayCallbacks(this); - } mWorkspace.setLauncherOverlay(overlay); } - @Override - public void runOnOverlayHidden(Runnable runnable) { - getWorkspace().runOnOverlayHidden(runnable); - } - public boolean setLauncherCallbacks(LauncherCallbacks callbacks) { mLauncherCallbacks = callbacks; return true; @@ -1213,18 +1205,6 @@ public class Launcher extends StatefulActivity mAppWidgetHolder.setActivityResumed(false); } - /** - * {@code LauncherOverlayCallbacks} scroll amount. - * Indicates transition progress to -1 screen. - * @param progress From 0 to 1. - */ - @Override - public void onScrollChanged(float progress) { - if (mWorkspace != null) { - mWorkspace.onOverlayScrollChanged(progress); - } - } - /** * Restores the previous state, if it exists. * @@ -2895,7 +2875,16 @@ public class Launcher extends StatefulActivity /** * Informs us that the overlay (-1 screen, typically), has either become visible or invisible. */ - public void onOverlayVisibilityChanged(boolean visible) {} + public void onOverlayVisibilityChanged(boolean visible) { + getStatsLogManager().logger() + .withSrcState(LAUNCHER_STATE_HOME) + .withDstState(LAUNCHER_STATE_HOME) + .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder() + .setWorkspace(WorkspaceContainer.newBuilder() + .setPageIndex(visible ? 0 : -1)) + .build()) + .log(visible ? LAUNCHER_SWIPELEFT : LAUNCHER_SWIPERIGHT); + } /** * Informs us that the page transition has ended, so that we can react to the newly selected diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index b6eb58973d..8f07a0df53 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.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; import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SWIPERIGHT; @@ -58,7 +57,6 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewTreeObserver; import android.view.accessibility.AccessibilityNodeInfo; import android.widget.FrameLayout; import android.widget.Toast; @@ -119,6 +117,7 @@ import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.dragndrop.AppWidgetHostViewDragListener; import com.android.launcher3.widget.util.WidgetSizes; import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlay; +import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks; import java.util.ArrayList; import java.util.Iterator; @@ -136,7 +135,7 @@ import java.util.stream.Collectors; public class Workspace extends PagedView implements DropTarget, DragSource, View.OnTouchListener, DragController.DragListener, Insettable, StateHandler, - WorkspaceLayoutManager, LauncherBindableItemsContainer { + WorkspaceLayoutManager, LauncherBindableItemsContainer, LauncherOverlayCallbacks { /** The value that {@link #mTransitionProgress} must be greater than for * {@link #transitionStateShouldAllowDrop()} to return true. */ @@ -254,14 +253,12 @@ public class Workspace extends PagedView // State related to Launcher Overlay private OverlayEdgeEffect mOverlayEdgeEffect; - boolean mOverlayShown = false; - private Runnable mOnOverlayHiddenCallback; + private boolean mOverlayShown = false; + private float mOverlayProgress; // 1 -> overlay completely visible, 0 -> home visible + private final List mOverlayCallbacks = new ArrayList<>(); private boolean mForceDrawAdjacentPages = false; - // Total over scrollX in the overlay direction. - private float mOverlayTranslation; - // Handles workspace state transitions private final WorkspaceStateTransitionAnimation mStateTransitionAnimation; @@ -1151,9 +1148,15 @@ public class Workspace extends PagedView } public void setLauncherOverlay(LauncherOverlay overlay) { - mOverlayEdgeEffect = overlay == null ? null : new OverlayEdgeEffect(getContext(), overlay); - EdgeEffectCompat newEffect = overlay == null - ? new EdgeEffectCompat(getContext()) : mOverlayEdgeEffect; + final EdgeEffectCompat newEffect; + if (overlay == null) { + newEffect = new EdgeEffectCompat(getContext()); + mOverlayEdgeEffect = null; + } else { + newEffect = mOverlayEdgeEffect = new OverlayEdgeEffect(getContext(), overlay); + overlay.setOverlayCallbacks(this); + } + if (mIsRtl) { mEdgeGlowRight = newEffect; } else { @@ -1203,132 +1206,46 @@ public class Workspace extends PagedView @Override protected boolean shouldFlingForVelocity(int velocityX) { // When the overlay is moving, the fling or settle transition is controlled by the overlay. - return Float.compare(Math.abs(mOverlayTranslation), 0) == 0 && - super.shouldFlingForVelocity(velocityX); + return Float.compare(Math.abs(mOverlayProgress), 0) == 0 + && super.shouldFlingForVelocity(velocityX); } /** * The overlay scroll is being controlled locally, just update our overlay effect */ + @Override public void onOverlayScrollChanged(float scroll) { - if (Float.compare(scroll, 1f) == 0) { + mOverlayProgress = Utilities.boundToRange(scroll, 0, 1); + if (Float.compare(mOverlayProgress, 1f) == 0) { if (!mOverlayShown) { - mLauncher.getStatsLogManager().logger() - .withSrcState(LAUNCHER_STATE_HOME) - .withDstState(LAUNCHER_STATE_HOME) - .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder() - .setWorkspace( - LauncherAtom.WorkspaceContainer.newBuilder() - .setPageIndex(0)) - .build()) - .log(LAUNCHER_SWIPELEFT); + mOverlayShown = true; + mLauncher.onOverlayVisibilityChanged(true); } - mOverlayShown = true; - - // Let the Launcher activity know that the overlay is now visible. - mLauncher.onOverlayVisibilityChanged(mOverlayShown); - - // Not announcing the overlay page for accessibility since it announces itself. - } else if (Float.compare(scroll, 0f) == 0) { + } else if (Float.compare(mOverlayProgress, 0f) == 0) { if (mOverlayShown) { - // TODO: this is logged unnecessarily on home gesture. - mLauncher.getStatsLogManager().logger() - .withSrcState(LAUNCHER_STATE_HOME) - .withDstState(LAUNCHER_STATE_HOME) - .withContainerInfo(LauncherAtom.ContainerInfo.newBuilder() - .setWorkspace( - LauncherAtom.WorkspaceContainer.newBuilder() - .setPageIndex(-1)) - .build()) - .log(LAUNCHER_SWIPERIGHT); - } else if (Float.compare(mOverlayTranslation, 0f) != 0) { - // When arriving to 0 overscroll from non-zero overscroll, announce page for - // accessibility since default announcements were disabled while in overscroll - // state. - // Not doing this if mOverlayShown because in that case the accessibility service - // will announce the launcher window description upon regaining focus after - // switching from the overlay screen. - announcePageForAccessibility(); + mOverlayShown = false; + mLauncher.onOverlayVisibilityChanged(false); } - mOverlayShown = false; - - // Let the Launcher activity know that the overlay is no longer visible. - mLauncher.onOverlayVisibilityChanged(mOverlayShown); - - tryRunOverlayCallback(); } - - float offset = 0f; - - scroll = Math.max(scroll - offset, 0); - scroll = Math.min(1, scroll / (1 - offset)); - - float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(scroll); - float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll; - - if (mIsRtl) { - transX = -transX; + int count = mOverlayCallbacks.size(); + for (int i = 0; i < count; i++) { + mOverlayCallbacks.get(i).onOverlayScrollChanged(mOverlayProgress); } - mOverlayTranslation = transX; - - // TODO(adamcohen): figure out a final effect here. We may need to recommend - // different effects based on device performance. On at least one relatively high-end - // device I've tried, translating the launcher causes things to get quite laggy. - mLauncher.getDragLayer().setTranslationX(transX); - mLauncher.getDragLayer().getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha); } /** - * @return false if the callback is still pending + * Adds a callback for receiving overlay progress */ - private boolean tryRunOverlayCallback() { - if (mOnOverlayHiddenCallback == null) { - // Return true as no callback is pending. This is used by OnWindowFocusChangeListener - // to remove itself if multiple focus handles were added. - return true; - } - if (mOverlayShown || !hasWindowFocus()) { - return false; - } - - mOnOverlayHiddenCallback.run(); - mOnOverlayHiddenCallback = null; - return true; + public void addOverlayCallback(LauncherOverlayCallbacks callback) { + mOverlayCallbacks.add(callback); + callback.onOverlayScrollChanged(mOverlayProgress); } /** - * Runs the given callback when the minus one overlay is hidden. Specifically, it is run - * when launcher's window has focus and the overlay is no longer being shown. If a callback - * is already present, the new callback will chain off it so both are run. - * - * @return Whether the callback was deferred. + * Removes a previously added overlay progress callback */ - public boolean runOnOverlayHidden(Runnable callback) { - if (mOnOverlayHiddenCallback == null) { - mOnOverlayHiddenCallback = callback; - } else { - // Chain the new callback onto the previous callback(s). - Runnable oldCallback = mOnOverlayHiddenCallback; - mOnOverlayHiddenCallback = () -> { - oldCallback.run(); - callback.run(); - }; - } - if (!tryRunOverlayCallback()) { - ViewTreeObserver observer = getViewTreeObserver(); - if (observer != null && observer.isAlive()) { - observer.addOnWindowFocusChangeListener( - new ViewTreeObserver.OnWindowFocusChangeListener() { - @Override - public void onWindowFocusChanged(boolean hasFocus) { - if (tryRunOverlayCallback() && observer.isAlive()) { - observer.removeOnWindowFocusChangeListener(this); - } - }}); - } - return true; - } - return false; + public void removeOverlayCallback(LauncherOverlayCallbacks callback) { + mOverlayCallbacks.remove(callback); } @Override @@ -3470,7 +3387,7 @@ public class Workspace extends PagedView protected boolean canAnnouncePageDescription() { // Disable announcements while overscrolling potentially to overlay screen because if we end // up on the overlay screen, it will take care of announcing itself. - return Float.compare(mOverlayTranslation, 0f) == 0; + return Float.compare(mOverlayProgress, 0f) == 0; } @Override diff --git a/src/com/android/launcher3/dragndrop/DragLayer.java b/src/com/android/launcher3/dragndrop/DragLayer.java index 4bea0adebc..1ee7fc1fa3 100644 --- a/src/com/android/launcher3/dragndrop/DragLayer.java +++ b/src/com/android/launcher3/dragndrop/DragLayer.java @@ -47,7 +47,9 @@ import com.android.launcher3.DropTargetBar; import com.android.launcher3.Launcher; import com.android.launcher3.R; import com.android.launcher3.ShortcutAndWidgetContainer; +import com.android.launcher3.Utilities; import com.android.launcher3.Workspace; +import com.android.launcher3.anim.Interpolators; import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.SpringProperty; import com.android.launcher3.celllayout.CellLayoutLayoutParams; @@ -56,13 +58,14 @@ import com.android.launcher3.graphics.Scrim; import com.android.launcher3.keyboard.ViewGroupFocusHelper; import com.android.launcher3.util.TouchController; import com.android.launcher3.views.BaseDragLayer; +import com.android.systemui.plugins.shared.LauncherOverlayManager.LauncherOverlayCallbacks; import java.util.ArrayList; /** * A ViewGroup that coordinates dragging across its descendants */ -public class DragLayer extends BaseDragLayer { +public class DragLayer extends BaseDragLayer implements LauncherOverlayCallbacks { public static final int ALPHA_INDEX_OVERLAY = 0; private static final int ALPHA_CHANNEL_COUNT = 1; @@ -70,6 +73,8 @@ public class DragLayer extends BaseDragLayer { public static final int ANIMATION_END_DISAPPEAR = 0; public static final int ANIMATION_END_REMAIN_VISIBLE = 2; + private final boolean mIsRtl; + private DragController mDragController; // Variables relating to animation of views after drop @@ -100,6 +105,7 @@ public class DragLayer extends BaseDragLayer { setChildrenDrawingOrderEnabled(true); mFocusIndicatorHelper = new ViewGroupFocusHelper(this); + mIsRtl = Utilities.isRtl(getResources()); } /** @@ -109,6 +115,7 @@ public class DragLayer extends BaseDragLayer { mDragController = dragController; recreateControllers(); mWorkspaceDragScrim = new Scrim(this); + workspace.addOverlayCallback(this); } @Override @@ -476,4 +483,16 @@ public class DragLayer extends BaseDragLayer { controller.onOneHandedModeStateChanged(activated); } } + + @Override + public void onOverlayScrollChanged(float progress) { + float alpha = 1 - Interpolators.DEACCEL_3.getInterpolation(progress); + float transX = getMeasuredWidth() * progress; + + if (mIsRtl) { + transX = -transX; + } + setTranslationX(transX); + getAlphaProperty(ALPHA_INDEX_OVERLAY).setValue(alpha); + } } diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java index 13e4999623..173b4540e6 100644 --- a/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java +++ b/src_plugins/com/android/systemui/plugins/shared/LauncherExterns.java @@ -40,10 +40,4 @@ public interface LauncherExterns { * Sets the overlay on the target activity */ void setLauncherOverlay(LauncherOverlay overlay); - - /** - * Executes the command, next time the overlay is hidden - */ - void runOnOverlayHidden(Runnable runnable); - } diff --git a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java index ac02ba4528..582ab230c3 100644 --- a/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java +++ b/src_plugins/com/android/systemui/plugins/shared/LauncherOverlayManager.java @@ -93,6 +93,6 @@ public interface LauncherOverlayManager extends Application.ActivityLifecycleCal interface LauncherOverlayCallbacks { - void onScrollChanged(float progress); + void onOverlayScrollChanged(float progress); } } From b0fb5096ba0dd30ae435cc5d0775529480f231a5 Mon Sep 17 00:00:00 2001 From: Holly Sun Date: Mon, 19 Sep 2022 14:53:32 -0700 Subject: [PATCH 12/14] Update gBoard icon to Go/Search depending on suggestions. If there is a highlight item, show Go (->) icon. Otherwise, show Search (magnifying glass) icon. Implement maybeUpdateEnterKeyIcon() in NexusLauncher because it's gBoard specific. Test: manual. Verify icon changes according to states. Bug: 183467628 Bug: 210661150 Change-Id: I5dbcb54a37f9d37ae31bf3eaef241ea8a3ede510 --- src/com/android/launcher3/config/FeatureFlags.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java index 32463a5746..4582af3b26 100644 --- a/src/com/android/launcher3/config/FeatureFlags.java +++ b/src/com/android/launcher3/config/FeatureFlags.java @@ -96,6 +96,10 @@ public final class FeatureFlags { "ENABLE_QUICK_LAUNCH_V2", false, "Use quick launch v2 " + "behavior. Quick search and quick launch v1 would be unavailable if this is enabled"); + public static final BooleanFlag GBOARD_UPDATE_ENTER_KEY = new DeviceFlag( + "GBOARD_UPDATE_ENTER_KEY", false, "Update gBoard enter key " + + "icon dynamically based on top search content for Quick Launch V2"); + public static final BooleanFlag ENABLE_HIDE_HEADER = new DeviceFlag("ENABLE_HIDE_HEADER", true, "Hide header on keyboard before typing in all apps"); From 7e8195081e7c3fd32dc2fc8f2066a13ea61da81f Mon Sep 17 00:00:00 2001 From: Jon Miranda Date: Thu, 3 Nov 2022 10:56:32 -0700 Subject: [PATCH 13/14] Update insets for transient taskbar Bug: 252905206 Test: manual Change-Id: I03bd493dbe63274c8e90cb5e415127a1dd93e9b8 --- quickstep/res/values/dimens.xml | 2 ++ .../launcher3/taskbar/TaskbarStashController.java | 12 +++++++++--- res/values/dimens.xml | 4 +++- src/com/android/launcher3/DeviceProfile.java | 12 ++++++++---- 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml index 32250787b3..6e3fd32329 100644 --- a/quickstep/res/values/dimens.xml +++ b/quickstep/res/values/dimens.xml @@ -288,6 +288,8 @@ 24dp 40dp 10dp + 32dp + 10dp 150dp 225dp diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java index afd659fc6e..72ae1d1c31 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java @@ -190,8 +190,13 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba if (isPhoneMode()) { // DeviceProfile's taskbar vars aren't initialized w/ the flag off Resources resources = mActivity.getResources(); - mUnstashedHeight = resources.getDimensionPixelSize(R.dimen.taskbar_size); - mStashedHeight = resources.getDimensionPixelOffset(R.dimen.taskbar_stashed_size); + boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity); + mUnstashedHeight = resources.getDimensionPixelSize(isTransientTaskbar + ? R.dimen.transient_taskbar_size + : R.dimen.taskbar_size); + mStashedHeight = resources.getDimensionPixelSize(isTransientTaskbar + ? R.dimen.transient_taskbar_stashed_size + : R.dimen.taskbar_stashed_size); } else { mUnstashedHeight = mActivity.getDeviceProfile().taskbarSize; mStashedHeight = mActivity.getDeviceProfile().stashedTaskbarSize; @@ -344,7 +349,8 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba * @see WindowInsets.Type#systemBars() */ public int getContentHeightToReportToApps() { - if (isPhoneMode() && !mActivity.isThreeButtonNav()) { + if ((isPhoneMode() && !mActivity.isThreeButtonNav()) + || DisplayController.isTransientTaskbar(mActivity)) { return getStashedHeight(); } diff --git a/res/values/dimens.xml b/res/values/dimens.xml index afdb0717b3..8f48ec8b2a 100644 --- a/res/values/dimens.xml +++ b/res/values/dimens.xml @@ -367,7 +367,9 @@ 0dp 0dp 0dp - 10dp + 0dp + + 0dp 8dp 0dp diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java index edd809c39b..2b9eb29051 100644 --- a/src/com/android/launcher3/DeviceProfile.java +++ b/src/com/android/launcher3/DeviceProfile.java @@ -312,10 +312,14 @@ public class DeviceProfile { } if (isTaskbarPresent) { - taskbarSize = DisplayController.isTransientTaskbar(context) - ? res.getDimensionPixelSize(R.dimen.transient_taskbar_size) - : res.getDimensionPixelSize(R.dimen.taskbar_size); - stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size); + if (DisplayController.isTransientTaskbar(context)) { + taskbarSize = res.getDimensionPixelSize(R.dimen.transient_taskbar_size); + stashedTaskbarSize = + res.getDimensionPixelSize(R.dimen.transient_taskbar_stashed_size); + } else { + taskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_size); + stashedTaskbarSize = res.getDimensionPixelSize(R.dimen.taskbar_stashed_size); + } } edgeMarginPx = res.getDimensionPixelSize(R.dimen.dynamic_grid_edge_margin); From 20ed998a514aeb8f72d8b3ae0f58353cc374f5b0 Mon Sep 17 00:00:00 2001 From: Ats Jenk Date: Thu, 3 Nov 2022 11:48:31 -0700 Subject: [PATCH 14/14] Clean up bubbles home gesture flag Flag is enabled by default. Removing as it is no longer used. Bug: 256873558 Test: mp launcher Change-Id: I8cdfde754aec7b683ae9e47aa8861ddc41460a59 --- .../quickstep/TouchInteractionService.java | 28 ++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 4cdb5f8d12..450774bee7 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -55,7 +55,6 @@ import android.os.Bundle; import android.os.IBinder; import android.os.Looper; import android.os.SystemClock; -import android.os.SystemProperties; import android.util.Log; import android.view.Choreographer; import android.view.InputEvent; @@ -138,9 +137,6 @@ public class TouchInteractionService extends Service private static final String TAG = "TouchInteractionService"; - private static final boolean BUBBLES_HOME_GESTURE_ENABLED = - SystemProperties.getBoolean("persist.wm.debug.bubbles_home_gesture", true); - private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once"; private final TISBinder mTISBinder = new TISBinder(); @@ -809,27 +805,9 @@ public class TouchInteractionService extends Service if (mDeviceState.isBubblesExpanded()) { reasonString = newCompoundString(reasonPrefix) .append(SUBSTRING_PREFIX) - .append("bubbles expanded"); - if (BUBBLES_HOME_GESTURE_ENABLED) { - reasonString.append(SUBSTRING_PREFIX) - .append("bubbles can handle the home gesture") - .append(", trying to use default input consumer"); - // Bubbles can handle home gesture itself. - base = getDefaultInputConsumer(reasonString); - } else { - // If Bubbles is expanded, use the overlay input consumer, which will close - // Bubbles instead of going all the way home when a swipe up is detected. - // Notification panel can be expanded on top of expanded bubbles. Bubbles remain - // expanded in the back. Make sure swipe up is not passed to bubbles in this - // case. - if (!mDeviceState.isNotificationPanelExpanded()) { - reasonString = newCompoundString(reasonPrefix) - .append(SUBSTRING_PREFIX) - .append("using SysUiOverlayInputConsumer"); - base = new SysUiOverlayInputConsumer( - getBaseContext(), mDeviceState, mInputMonitorCompat); - } - } + .append("bubbles expanded, trying to use default input consumer"); + // Bubbles can handle home gesture itself. + base = getDefaultInputConsumer(reasonString); } if (mDeviceState.isSystemUiDialogShowing()) {