Merge "Implement hotseat prediction client" into ub-launcher3-master
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
deleted file mode 100644
index 76050d5..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherInitListenerEx.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package com.android.launcher3;
-
-import com.android.launcher3.appprediction.PredictionUiStateManager;
-import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
-
-import java.util.function.BiPredicate;
-
-public class LauncherInitListenerEx extends LauncherInitListener {
-
- public LauncherInitListenerEx(BiPredicate<Launcher, Boolean> onInitListener) {
- super(onInitListener);
- }
-
- @Override
- public boolean init(Launcher launcher, boolean alreadyOnHome) {
- PredictionUiStateManager.INSTANCE.get(launcher).switchClient(Client.OVERVIEW);
- return super.init(launcher, alreadyOnHome);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
index 1279270..7b4bb02 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/states/QuickSwitchState.java
@@ -26,7 +26,7 @@
/**
* State to indicate we are about to launch a recent task. Note that this state is only used when
- * quick switching from launcher; quick switching from an app uses WindowTransformSwipeHelper.
+ * quick switching from launcher; quick switching from an app uses LauncherSwipeHandler.
* @see GestureState.GestureEndTarget#NEW_TASK
*/
public class QuickSwitchState extends BackgroundAppState {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
similarity index 95%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index b6cd456..24f247b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/FallbackNoButtonInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.quickstep.inputconsumers;
+package com.android.quickstep;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
@@ -22,7 +22,7 @@
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
@@ -40,26 +40,19 @@
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.ObjectWrapper;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
-import com.android.quickstep.AnimatedFloat;
-import com.android.quickstep.BaseSwipeUpHandler;
-import com.android.quickstep.GestureState;
import com.android.quickstep.GestureState.GestureEndTarget;
-import com.android.quickstep.InputConsumer;
-import com.android.quickstep.MultiStateCallback;
-import com.android.quickstep.RecentsActivity;
-import com.android.quickstep.RecentsAnimationController;
-import com.android.quickstep.RecentsAnimationDeviceState;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.InputConsumerController;
-public class FallbackNoButtonInputConsumer extends
- BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
+/**
+ * Handles the navigation gestures when a 3rd party launcher is the default home activity.
+ */
+public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[5] : null;
@@ -108,7 +101,7 @@
private final PointF mEndVelocityPxPerMs = new PointF(0, 0.5f);
private RunningWindowAnim mFinishAnimation;
- public FallbackNoButtonInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
+ public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, InputConsumerController inputConsumer,
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
super(context, deviceState, gestureState, inputConsumer);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
index 48b8fc6..01cf4bb 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherActivityInterface.java
@@ -29,7 +29,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL_DEACCEL;
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.WindowTransformSwipeHandler.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
import android.animation.Animator;
import android.animation.AnimatorSet;
@@ -49,12 +49,13 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
-import com.android.launcher3.LauncherInitListenerEx;
+import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
@@ -413,7 +414,7 @@
@Override
public ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener) {
- return new LauncherInitListenerEx((activity, alreadyOnHome) ->
+ return new LauncherInitListener((activity, alreadyOnHome) ->
onInitListener.test(alreadyOnHome));
}
@@ -541,4 +542,14 @@
}
launcher.setOnDeferredActivityLaunchCallback(r);
}
+
+ @Override
+ public void updateOverviewPredictionState() {
+ Launcher launcher = getCreatedActivity();
+ if (launcher == null) {
+ return;
+ }
+ PredictionUiStateManager.INSTANCE.get(launcher).switchClient(
+ PredictionUiStateManager.Client.OVERVIEW);
+ }
}
\ No newline at end of file
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
similarity index 96%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
index b55ec20..3eb183e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandler.java
@@ -32,6 +32,7 @@
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import android.animation.Animator;
@@ -83,10 +84,13 @@
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+/**
+ * Handles the navigation gestures when Launcher is the default home activity.
+ */
@TargetApi(Build.VERSION_CODES.O)
-public class WindowTransformSwipeHandler<T extends BaseDraggingActivity>
+public class LauncherSwipeHandler<T extends BaseDraggingActivity>
extends BaseSwipeUpHandler<T, RecentsView> implements OnApplyWindowInsetsListener {
- private static final String TAG = WindowTransformSwipeHandler.class.getSimpleName();
+ private static final String TAG = LauncherSwipeHandler.class.getSimpleName();
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
@@ -189,7 +193,7 @@
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
- public WindowTransformSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
+ public LauncherSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer) {
@@ -293,6 +297,12 @@
}
setupRecentsViewUi();
+
+ if (mDeviceState.getNavMode() == TWO_BUTTONS) {
+ // If the device is in two button mode, swiping up will show overview with predictions
+ // so we need to kick off switching to the overview predictions as soon as possible
+ mActivityInterface.updateOverviewPredictionState();
+ }
return true;
}
@@ -423,6 +433,12 @@
@Override
public void onMotionPauseChanged(boolean isPaused) {
setShelfState(isPaused ? PEEK : HIDE, OVERSHOOT_1_2, SHELF_ANIM_DURATION);
+
+ if (mDeviceState.isFullyGesturalNavMode() && isPaused) {
+ // In fully gestural nav mode, switch to overview predictions once the user has paused
+ // (this is a no-op if the predictions are already in that state)
+ mActivityInterface.updateOverviewPredictionState();
+ }
}
public void maybeUpdateRecentsAttachedState() {
@@ -666,7 +682,7 @@
endLauncherTransitionController();
if (!ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// Hide the task view, if not already hidden
- setTargetAlphaProvider(WindowTransformSwipeHandler::getHiddenTargetAlpha);
+ setTargetAlphaProvider(LauncherSwipeHandler::getHiddenTargetAlpha);
}
BaseDraggingActivity activity = mActivityInterface.getCreatedActivity();
@@ -1028,6 +1044,15 @@
@UiThread
private void startNewTask() {
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mRecentsAnimationController.finish(true /* toRecents */, this::startNewTaskInternal);
+ } else {
+ startNewTaskInternal();
+ }
+ }
+
+ @UiThread
+ private void startNewTaskInternal() {
startNewTask(STATE_HANDLER_INVALIDATED, success -> {
if (!success) {
// We couldn't launch the task, so take user to overview so they can
@@ -1163,20 +1188,21 @@
private void finishCurrentTransitionToRecents() {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
- } else if (!hasTargets()) {
- // If there are no targets, then there is nothing to finish
+ } else if (!hasTargets() || mRecentsAnimationController == null) {
+ // If there are no targets or the animation not started, then there is nothing to finish
mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
} else {
- synchronized (mRecentsAnimationController) {
- mRecentsAnimationController.finish(true /* toRecents */,
- () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
- }
+ mRecentsAnimationController.finish(true /* toRecents */,
+ () -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED));
}
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", true);
}
private void finishCurrentTransitionToHome() {
- synchronized (mRecentsAnimationController) {
+ if (!hasTargets() || mRecentsAnimationController == null) {
+ // If there are no targets or the animation not started, then there is nothing to finish
+ mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED);
+ } else {
mRecentsAnimationController.finish(true /* toRecents */,
() -> mStateCallback.setStateOnUiThread(STATE_CURRENT_TASK_FINISHED),
true /* sendUserLeaveHint */);
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
index 7f53d83..c4733bd 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
@@ -29,6 +29,7 @@
import androidx.annotation.BinderThread;
import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.util.ActivityInitListener;
@@ -212,6 +213,10 @@
LauncherLogProto.ContainerType.TASKSWITCHER);
mUserEventLogged = true;
}
+
+ // Switch prediction client to overview
+ PredictionUiStateManager.INSTANCE.get(activity).switchClient(
+ PredictionUiStateManager.Client.OVERVIEW);
return mAnimationProvider.onActivityReady(activity, wasVisible);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
index 7ad3f37..71f8ba4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -66,7 +66,6 @@
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
import com.android.quickstep.inputconsumers.DeviceLockedInputConsumer;
-import com.android.quickstep.inputconsumers.FallbackNoButtonInputConsumer;
import com.android.quickstep.inputconsumers.OtherActivityInputConsumer;
import com.android.quickstep.inputconsumers.OverscrollInputConsumer;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
@@ -242,10 +241,10 @@
return sIsInitialized;
}
- private final BaseSwipeUpHandler.Factory mWindowTransformFactory =
- this::createWindowTransformSwipeHandler;
- private final BaseSwipeUpHandler.Factory mFallbackNoButtonFactory =
- this::createFallbackNoButtonSwipeHandler;
+ private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
+ this::createLauncherSwipeHandler;
+ private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
+ this::createFallbackSwipeHandler;
private ActivityManagerWrapper mAM;
private OverviewCommandHelper mOverviewCommandHelper;
@@ -564,11 +563,11 @@
if (mDeviceState.isFullyGesturalNavMode()
&& !mOverviewComponentObserver.isHomeAndOverviewSame()) {
shouldDefer = previousGestureState.getFinishingRecentsAnimationTaskId() < 0;
- factory = mFallbackNoButtonFactory;
+ factory = mFallbackSwipeHandlerFactory;
} else {
shouldDefer = gestureState.getActivityInterface().deferStartingActivity(mDeviceState,
event);
- factory = mWindowTransformFactory;
+ factory = mLauncherSwipeHandlerFactory;
}
final boolean disableHorizontalSwipe = mDeviceState.isInExclusionRegion(event);
@@ -716,15 +715,15 @@
}
}
- private BaseSwipeUpHandler createWindowTransformSwipeHandler(GestureState gestureState,
+ private BaseSwipeUpHandler createLauncherSwipeHandler(GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new WindowTransformSwipeHandler(this, mDeviceState, mTaskAnimationManager,
+ return new LauncherSwipeHandler(this, mDeviceState, mTaskAnimationManager,
gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
}
- private BaseSwipeUpHandler createFallbackNoButtonSwipeHandler(GestureState gestureState,
+ private BaseSwipeUpHandler createFallbackSwipeHandler(GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture, boolean isLikelyToStartNewTask) {
- return new FallbackNoButtonInputConsumer(this, mDeviceState, gestureState,
+ return new FallbackSwipeHandler(this, mDeviceState, gestureState,
mInputConsumer, isLikelyToStartNewTask, continuingLastGesture);
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
index e448c5b..5a34520 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/DeviceLockedInputConsumer.java
@@ -22,7 +22,7 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
-import static com.android.quickstep.WindowTransformSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
import android.content.ComponentName;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
index c875ba0..cf616fe 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/RecentsView.java
@@ -1742,7 +1742,17 @@
return;
}
- mRecentsAnimationController.finish(toRecents, onFinishComplete);
+ mRecentsAnimationController.finish(toRecents, () -> {
+ if (onFinishComplete != null) {
+ onFinishComplete.run();
+ // After we finish the recents animation, the current task id should be correctly
+ // reset so that when the task is launched from Overview later, it goes through the
+ // flow of starting a new task instead of finishing recents animation to app. A
+ // typical example of this is (1) user swipes up from app to Overview (2) user
+ // taps on QSB (3) user goes back to Overview and launch the most recent task.
+ setCurrentTask(-1);
+ }
+ });
}
public void setDisallowScrollToClearAll(boolean disallowScrollToClearAll) {
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
index 57327f8..a1775f4 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/TaskView.java
@@ -287,11 +287,19 @@
public void launchTask(boolean animate, boolean freezeTaskList, Consumer<Boolean> resultCallback,
Handler resultCallbackHandler) {
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ RecentsView recentsView = getRecentsView();
if (isRunningTask()) {
- getRecentsView().finishRecentsAnimation(false /* toRecents */,
+ recentsView.finishRecentsAnimation(false /* toRecents */,
() -> resultCallbackHandler.post(() -> resultCallback.accept(true)));
} else {
- launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
+ // This is a workaround against the WM issue that app open is not correctly animated
+ // when recents animation is being cleaned up (b/143774568). When that's possible,
+ // we should rely on the framework side to cancel the recents animation, and we will
+ // clean up the screenshot on the launcher side while we launch the next task.
+ recentsView.switchToScreenshot(null,
+ () -> recentsView.finishRecentsAnimation(true /* toRecents */,
+ () -> launchTaskInternal(animate, freezeTaskList, resultCallback,
+ resultCallbackHandler)));
}
} else {
launchTaskInternal(animate, freezeTaskList, resultCallback, resultCallbackHandler);
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 663b125..96340b2 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -22,6 +22,7 @@
import android.os.CancellationSignal;
import android.os.Handler;
+import com.android.launcher3.util.ActivityTracker;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.RemoteAnimationProvider;
@@ -32,6 +33,11 @@
private RemoteAnimationProvider mRemoteAnimationProvider;
+ /**
+ * @param onInitListener a callback made when the activity is initialized. The callback should
+ * return true to continue receiving callbacks (ie. for if the activity is
+ * recreated).
+ */
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
super(onInitListener, Launcher.ACTIVITY_TRACKER);
}
diff --git a/quickstep/src/com/android/launcher3/model/WellbeingModel.java b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
index 852a08e..5aa4388 100644
--- a/quickstep/src/com/android/launcher3/model/WellbeingModel.java
+++ b/quickstep/src/com/android/launcher3/model/WellbeingModel.java
@@ -335,8 +335,8 @@
* Shortcut factory for generating wellbeing action
*/
public static final SystemShortcut.Factory SHORTCUT_FACTORY = (activity, info) ->
- WellbeingModel.get(activity).getShortcutForApp(
- info.getTargetComponent().getPackageName(),
- info.user.getIdentifier(),
- activity, info);
+ (info.getTargetComponent() == null) ? null : WellbeingModel.get(activity)
+ .getShortcutForApp(
+ info.getTargetComponent().getPackageName(), info.user.getIdentifier(),
+ activity, info);
}
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index cb18001..86ffd22 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -89,6 +89,13 @@
}
/**
+ * Updates the prediction state to the overview state.
+ */
+ default void updateOverviewPredictionState() {
+ // By default overview predictions are not supported
+ }
+
+ /**
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
*/
int getContainerType();
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index fe37d60..b1c72ce 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -31,6 +31,11 @@
private final BiPredicate<T, Boolean> mOnInitListener;
private final ActivityTracker<T> mActivityTracker;
+ /**
+ * @param onInitListener a callback made when the activity is initialized. The callback should
+ * return true to continue receiving callbacks (ie. for if the activity is
+ * recreated).
+ */
public ActivityInitListener(BiPredicate<T, Boolean> onInitListener,
ActivityTracker<T> tracker) {
mOnInitListener = onInitListener;
@@ -42,6 +47,10 @@
return mOnInitListener.test(activity, alreadyOnHome);
}
+ /**
+ * Registers the activity-created listener. If the activity is already created, then the
+ * callback provided in the constructor will be called synchronously.
+ */
public void register() {
mActivityTracker.schedule(this);
}
diff --git a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
index 13731b6..ca81343 100644
--- a/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
+++ b/quickstep/tests/src/com/android/quickstep/FallbackRecentsTest.java
@@ -175,7 +175,7 @@
}
result[0] = f.apply(activity);
return true;
- }).get(), DEFAULT_UI_TIMEOUT);
+ }).get(), DEFAULT_UI_TIMEOUT, mLauncher);
return (T) result[0];
}
@@ -196,7 +196,7 @@
startAppFastAndWaitForRecentTask(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
startTestActivity(2);
Wait.atMost("Expected three apps in the task list",
- () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT);
+ () -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
BaseOverview overview = mLauncher.getBackground().switchToOverview();
executeOnRecents(recents ->
@@ -255,7 +255,8 @@
private void startAppFastAndWaitForRecentTask(String packageName) {
startAppFast(packageName);
Wait.atMost("Expected app in task list",
- () -> containsRecentTaskWithPackage(packageName), DEFAULT_ACTIVITY_TIMEOUT);
+ () -> containsRecentTaskWithPackage(packageName), DEFAULT_ACTIVITY_TIMEOUT,
+ mLauncher);
}
private boolean containsRecentTaskWithPackage(String packageName) {
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index c2197ab..5158939 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -80,7 +80,8 @@
Mode mode = description.getAnnotation(NavigationModeSwitch.class).mode();
return new Statement() {
private void assertTrue(String message, boolean condition) {
- if(!condition) {
+ mLauncher.checkForAnomaly();
+ if (!condition) {
final AssertionError assertionError = new AssertionError(message);
FailureWatcher.onError(mLauncher.getDevice(), description, assertionError);
throw assertionError;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 3dce9fc..8714032 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -36,7 +36,6 @@
import static com.android.launcher3.popup.SystemShortcut.INSTALL;
import static com.android.launcher3.popup.SystemShortcut.WIDGETS;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
-import static com.android.launcher3.testing.TestProtocol.CRASH_ADD_CUSTOM_SHORTCUT;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -121,7 +120,6 @@
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.states.RotationHelper;
-import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
@@ -1210,13 +1208,8 @@
*/
private void completeAddShortcut(Intent data, int container, int screenId, int cellX,
int cellY, PendingRequestArgs args) {
- if (data == null
- || args.getRequestCode() != REQUEST_CREATE_SHORTCUT
+ if (args.getRequestCode() != REQUEST_CREATE_SHORTCUT
|| args.getPendingIntent().getComponent() == null) {
- if (data == null && TestProtocol.sDebugTracing) {
- Log.d(CRASH_ADD_CUSTOM_SHORTCUT,
- "Failed to add custom shortcut: Intent is null, args = " + args);
- }
return;
}
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index b46f465..fa0fe1b 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -140,6 +140,15 @@
// or app was already installed for another user.
itemInfo = new AppInfo(app.getContext(), activities.get(0), item.user)
.makeWorkspaceItem();
+
+ if (shortcutExists(dataModel, itemInfo.getIntent(), itemInfo.user)) {
+ // We need this additional check here since we treat all auto added
+ // workspace items as promise icons. At this point we now have the
+ // correct intent to compare against existing workspace icons.
+ // Icon already exists on the workspace and should not be auto-added.
+ continue;
+ }
+
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.bitmap = app.getIconCache().getDefaultIcon(item.user);
diff --git a/src/com/android/launcher3/popup/ArrowPopup.java b/src/com/android/launcher3/popup/ArrowPopup.java
index 28000b9..98f7fd8 100644
--- a/src/com/android/launcher3/popup/ArrowPopup.java
+++ b/src/com/android/launcher3/popup/ArrowPopup.java
@@ -360,10 +360,14 @@
final TimeInterpolator revealInterpolator = ACCEL_DEACCEL;
// Rectangular reveal.
+ mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
final ValueAnimator revealAnim = createOpenCloseOutlineProvider()
.createRevealAnimator(this, false);
revealAnim.setDuration(revealDuration);
revealAnim.setInterpolator(revealInterpolator);
+ // Clip the popup to the initial outline while the notification dot and arrow animate.
+ revealAnim.start();
+ revealAnim.pause();
ValueAnimator fadeIn = ValueAnimator.ofFloat(0, 1);
fadeIn.setDuration(revealDuration + arrowDuration);
@@ -399,7 +403,6 @@
if (!mIsOpen) {
return;
}
- mEndRect.setEmpty();
if (getOutlineProvider() instanceof RevealOutlineAnimation) {
((RevealOutlineAnimation) getOutlineProvider()).getOutline(mEndRect);
}
@@ -471,9 +474,6 @@
mStartRect.set(arrowCenterX - halfArrowWidth, arrowCenterY, arrowCenterX + halfArrowWidth,
arrowCenterY);
- if (mEndRect.isEmpty()) {
- mEndRect.set(0, 0, getMeasuredWidth(), getMeasuredHeight());
- }
return new RoundedRectRevealOutlineProvider
(arrowCornerRadius, mOutlineRadius, mStartRect, mEndRect);
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 79b41c1..e70673a 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -570,8 +570,11 @@
@Override
protected void closeComplete() {
- mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
- mOriginalIcon.setForceHideDot(false);
+ PopupContainerWithArrow openPopup = getOpen(mLauncher);
+ if (openPopup == null || openPopup.mOriginalIcon != mOriginalIcon) {
+ mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
+ mOriginalIcon.setForceHideDot(false);
+ }
super.closeComplete();
}
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index d686e95..1cfa4af 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -84,5 +84,4 @@
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String NO_DRAG_TO_WORKSPACE = "b/138729456";
public static final String APP_NOT_DISABLED = "b/139891609";
- public static final String CRASH_ADD_CUSTOM_SHORTCUT = "b/141568904";
}
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index 4b931f4..499f655 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -50,22 +50,41 @@
}
}
- public void schedule(SchedulerCallback<? extends T> callback) {
+ /**
+ * Schedules the callback to be notified when the activity is created.
+ * @return true if the activity is already created, false otherwise
+ */
+ public boolean schedule(SchedulerCallback<? extends T> callback) {
synchronized (this) {
mPendingCallback = new WeakReference<>((SchedulerCallback<T>) callback);
}
- MAIN_EXECUTOR.execute(this);
+ if (!notifyInitIfPending()) {
+ // If the activity doesn't already exist, then post and wait for the activity to start
+ MAIN_EXECUTOR.execute(this);
+ return false;
+ }
+ return true;
}
@Override
public void run() {
- T activity = mCurrentActivity.get();
- if (activity != null) {
- initIfPending(activity, activity.isStarted());
- }
+ notifyInitIfPending();
}
- public boolean initIfPending(T activity, boolean alreadyOnHome) {
+ /**
+ * Notifies the pending callback if the activity is now created.
+ * @return true if the activity is now created.
+ */
+ private boolean notifyInitIfPending() {
+ T activity = mCurrentActivity.get();
+ if (activity != null) {
+ notifyInitIfPending(activity, activity.isStarted());
+ return true;
+ }
+ return false;
+ }
+
+ public boolean notifyInitIfPending(T activity, boolean alreadyOnHome) {
SchedulerCallback<T> pendingCallback = mPendingCallback.get();
if (pendingCallback != null) {
if (!pendingCallback.init(activity, alreadyOnHome)) {
@@ -114,7 +133,7 @@
}
}
if (!result && !explicitIntent) {
- result = initIfPending(activity, alreadyOnHome);
+ result = notifyInitIfPending(activity, alreadyOnHome);
}
return result;
}
diff --git a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
index 76b5d28..bb19515 100644
--- a/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
+++ b/tests/src/com/android/launcher3/ui/AbstractLauncherUiTest.java
@@ -26,8 +26,6 @@
import static java.lang.System.exit;
-import static androidx.test.InstrumentationRegistry.getInstrumentation;
-
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -306,7 +304,7 @@
protected void waitForLauncherCondition(
String message, Function<Launcher, Boolean> condition, long timeout) {
if (!TestHelpers.isInLauncherProcess()) return;
- Wait.atMost(message, () -> getFromLauncher(condition), timeout);
+ Wait.atMost(message, () -> getFromLauncher(condition), timeout, mLauncher);
}
// Cannot be used in TaplTests after injecting any gesture using Tapl because this can hide
@@ -319,7 +317,7 @@
final Object fromLauncher = getFromLauncher(f);
output[0] = fromLauncher;
return fromLauncher != null;
- }, timeout);
+ }, timeout, mLauncher);
return (T) output[0];
}
@@ -333,7 +331,7 @@
Wait.atMost(message, () -> {
testThreadAction.run();
return getFromLauncher(condition);
- }, timeout);
+ }, timeout, mLauncher);
}
protected LauncherActivityInfo getSettingsApp() {
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 29da0fa..191b405 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -356,9 +356,9 @@
* Custom shortcuts are replaced by deep shortcuts after api 25.
*/
@Test
- @Ignore("Temporarily disabled to unblock merging to master")
@PortraitLandscape
public void testDragCustomShortcut() {
+ if (!TestHelpers.isInLauncherProcess()) return; // b/143725213
mLauncher.getWorkspace().openAllWidgets()
.getWidget("com.android.launcher3.testcomponent.CustomShortcutConfigActivity")
.dragToWorkspace();
diff --git a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
index e1b3ede..0472ce1 100644
--- a/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/AddConfigWidgetTest.java
@@ -103,12 +103,12 @@
setResult(acceptConfig);
if (acceptConfig) {
- Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT);
+ Wait.atMost(null, new WidgetSearchCondition(), DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
assertNotNull(mAppWidgetManager.getAppWidgetInfo(mWidgetId));
} else {
// Verify that the widget id is deleted.
Wait.atMost(null, () -> mAppWidgetManager.getAppWidgetInfo(mWidgetId) == null,
- DEFAULT_ACTIVITY_TIMEOUT);
+ DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
}
}
diff --git a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
index 07129dd..d909ad7 100644
--- a/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/RequestPinItemTest.java
@@ -59,7 +59,8 @@
@RunWith(AndroidJUnit4.class)
public class RequestPinItemTest extends AbstractLauncherUiTest {
- @Rule public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
+ @Rule
+ public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private String mCallbackAction;
private String mShortcutId;
@@ -84,10 +85,10 @@
.equals(AppWidgetNoConfig.class.getName()));
}
- @Test
+ @Test
public void testPinWidgetNoConfig_customPreview() throws Throwable {
// Command to set custom preview
- Intent command = RequestPinItemActivity.getCommandIntent(
+ Intent command = RequestPinItemActivity.getCommandIntent(
RequestPinItemActivity.class, "setRemoteViewColor").putExtra(
RequestPinItemActivity.EXTRA_PARAM + "0", Color.RED);
@@ -169,7 +170,8 @@
// Go back to home
mLauncher.pressHome();
- Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT);
+ Wait.atMost(null, new ItemSearchCondition(itemMatcher), DEFAULT_ACTIVITY_TIMEOUT,
+ mLauncher);
}
/**
diff --git a/tests/src/com/android/launcher3/util/Wait.java b/tests/src/com/android/launcher3/util/Wait.java
index 899686b..2663d02 100644
--- a/tests/src/com/android/launcher3/util/Wait.java
+++ b/tests/src/com/android/launcher3/util/Wait.java
@@ -3,6 +3,8 @@
import android.os.SystemClock;
import android.util.Log;
+import com.android.launcher3.tapl.LauncherInstrumentation;
+
import org.junit.Assert;
/**
@@ -12,11 +14,13 @@
private static final long DEFAULT_SLEEP_MS = 200;
- public static void atMost(String message, Condition condition, long timeout) {
- atMost(message, condition, timeout, DEFAULT_SLEEP_MS);
+ public static void atMost(String message, Condition condition, long timeout,
+ LauncherInstrumentation launcher) {
+ atMost(message, condition, timeout, DEFAULT_SLEEP_MS, launcher);
}
- public static void atMost(String message, Condition condition, long timeout, long sleepMillis) {
+ public static void atMost(String message, Condition condition, long timeout, long sleepMillis,
+ LauncherInstrumentation launcher) {
final long startTime = SystemClock.uptimeMillis();
long endTime = startTime + timeout;
Log.d("Wait", "atMost: " + startTime + " - " + endTime);
@@ -40,6 +44,7 @@
throw new RuntimeException(t);
}
Log.d("Wait", "atMost: timed out: " + SystemClock.uptimeMillis());
+ launcher.checkForAnomaly();
Assert.fail(message);
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 3713c14..1c851f4 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -64,10 +64,12 @@
import com.android.launcher3.testing.TestProtocol;
import com.android.systemui.shared.system.QuickStepContract;
-import java.util.ArrayList;
+import org.junit.Assert;
+
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Deque;
@@ -77,8 +79,6 @@
import java.util.function.Consumer;
import java.util.function.Function;
-import org.junit.Assert;
-
/**
* The main tapl object. The only object that can be explicitly constructed by the using code. It
* produces all other objects.
@@ -298,6 +298,14 @@
return null;
}
+ public void checkForAnomaly() {
+ final String anomalyMessage = getAnomalyMessage();
+ if (anomalyMessage != null) {
+ failWithSystemHealth(
+ "Tests are broken by a non-Launcher system error: " + anomalyMessage);
+ }
+ }
+
private String getVisibleStateMessage() {
if (hasLauncherObject(WIDGETS_RES_ID)) return "Widgets";
if (hasLauncherObject(OVERVIEW_RES_ID)) return "Overview";
@@ -331,20 +339,17 @@
}
private void fail(String message) {
- message = "http://go/tapl : " + getContextDescription() + message;
+ checkForAnomaly();
- final String anomaly = getAnomalyMessage();
- if (anomaly != null) {
- message = anomaly + ", which causes:\n" + message;
- } else {
- message = message + " (visible state: " + getVisibleStateMessage() + ")";
- }
+ failWithSystemHealth("http://go/tapl : " + getContextDescription() + message +
+ " (visible state: " + getVisibleStateMessage() + ")");
+ }
+ private void failWithSystemHealth(String message) {
final String systemHealth = getSystemHealthMessage();
if (systemHealth != null) {
message = message
- + ", which might be a consequence of system health "
- + "problems:\n<<<<<<<<<<<<<<<<<<\n"
+ + ", perhaps because of system health problems:\n<<<<<<<<<<<<<<<<<<\n"
+ systemHealth + "\n>>>>>>>>>>>>>>>>>>";
}
@@ -531,8 +536,7 @@
// accessibility events prior to pressing Home.
final String action;
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
- final String anomaly = getAnomalyMessage();
- if (anomaly != null) fail("Can't swipe up to Home: " + anomaly);
+ checkForAnomaly();
final Point displaySize = getRealDisplaySize();