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/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/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/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/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/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/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/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; - } } 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/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 + " ]"); } 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..450774bee7 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; @@ -22,6 +23,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; @@ -46,7 +48,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; @@ -54,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; @@ -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; @@ -139,21 +137,7 @@ 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 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 +248,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 +461,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 +498,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 +580,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; @@ -829,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()) { @@ -927,6 +885,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 +1212,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/interaction/AllSetActivity.java b/quickstep/src/com/android/quickstep/interaction/AllSetActivity.java index 7ff576ef21..8986c054dd 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()); 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; } } 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..35414a644a 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 finishRecentsAnimation(true /* toRecents */, + false /* shouldPip */, null /* onFinishComplete */)); + } + } + }); anim.addEndListener(success -> { if (success) { InteractionJankMonitorWrapper.end( @@ -4190,8 +4200,6 @@ public abstract class RecentsView0dp 0dp 0dp - 10dp + 0dp + + 0dp 8dp 0dp diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index e66d441912..de2a1f91ed 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; } @@ -2544,21 +2521,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/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); 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..27e1ba143b 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 @@ -2467,23 +2384,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, @@ -3470,7 +3384,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/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 32463a5746..4287779c31 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"); @@ -290,6 +294,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" 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/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/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); } } 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(