Merge "Recycling already running activities" into ub-launcher3-master
diff --git a/OWNERS b/OWNERS
index 3069afa..1d6ad8c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -28,6 +28,7 @@
peanutbutter@google.com
xuqiu@google.com
sreyasr@google.com
+thiruram@google.com
per-file FeatureFlags.java, globs = set noparent
per-file FeatureFlags.java = sunnygoyal@google.com, winsonc@google.com, zakcohen@google.com, mrcasey@google.com, adamcohen@google.com, hyunyoungs@google.com
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
index c1a585e..e45fa9d 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/NavBarToHomeTouchController.java
@@ -159,8 +159,7 @@
builder.setFloat(recentsView, ADJACENT_PAGE_OFFSET,
-mPullbackDistance / recentsView.getPageOffsetScale(), PULLBACK_INTERPOLATOR);
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- builder.addOnFrameCallback(
- () -> recentsView.redrawLiveTile(false /* mightNeedToRefill */));
+ builder.addOnFrameCallback(recentsView::redrawLiveTile);
}
} else if (mStartState == ALL_APPS) {
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
index 0ee5d04..37a595e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/uioverrides/touchcontrollers/TaskViewTouchController.java
@@ -263,8 +263,10 @@
totalDisplacement * mProgressMultiplier, 0, 1));
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (mRecentsView.getCurrentPage() != 0 || isGoingUp) {
- mRecentsView.redrawLiveTile(true);
+ if (mRecentsView.getCurrentPage() == 0) {
+ mRecentsView.getLiveTileTaskViewSimulator().setOffsetY(
+ isGoingUp ? totalDisplacement : 0);
+ mRecentsView.redrawLiveTile();
}
}
return true;
@@ -300,7 +302,7 @@
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mCurrentAnimation.getAnimationPlayer().addUpdateListener(valueAnimator -> {
if (mRecentsView.getCurrentPage() != 0 || mCurrentAnimationIsGoingUp) {
- mRecentsView.redrawLiveTile(true);
+ mRecentsView.redrawLiveTile();
}
});
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
similarity index 81%
rename from quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
index 27d4846..5a3449b 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -15,6 +15,8 @@
*/
package com.android.quickstep;
+import static android.widget.Toast.LENGTH_SHORT;
+
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
import static com.android.launcher3.BaseActivity.STATE_HANDLER_INVISIBILITY_FLAGS;
import static com.android.launcher3.anim.Interpolators.DEACCEL;
@@ -29,7 +31,9 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_LEFT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
+import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.SystemUiController.UI_STATE_OVERVIEW;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.GestureState.GestureEndTarget.HOME;
import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
@@ -51,13 +55,17 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.PointF;
+import android.graphics.Rect;
import android.os.Build;
import android.os.SystemClock;
+import android.util.Log;
+import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
import android.view.WindowInsets;
import android.view.animation.Interpolator;
+import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
@@ -73,17 +81,24 @@
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.statemanager.StatefulActivity;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TraceHelper;
+import com.android.launcher3.util.VibratorWrapper;
+import com.android.launcher3.util.WindowBounds;
import com.android.quickstep.BaseActivityInterface.AnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
import com.android.quickstep.inputconsumers.OverviewInputConsumer;
import com.android.quickstep.util.ActiveGestureLog;
+import com.android.quickstep.util.ActivityInitListener;
+import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
+import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.TransformParams;
import com.android.quickstep.views.LiveTileOverlay;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -95,17 +110,34 @@
import com.android.systemui.shared.system.TaskInfoCompat;
import com.android.systemui.shared.system.TaskStackChangeListener;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
/**
* Handles the navigation gestures when Launcher is the default home activity.
- * TODO: Merge this with BaseSwipeUpHandler
*/
-@TargetApi(Build.VERSION_CODES.O)
-public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q extends RecentsView>
- extends BaseSwipeUpHandler<T, Q> implements OnApplyWindowInsetsListener {
- private static final String TAG = BaseSwipeUpHandlerV2.class.getSimpleName();
+@TargetApi(Build.VERSION_CODES.R)
+public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
+ extends SwipeUpAnimationLogic implements OnApplyWindowInsetsListener,
+ RecentsAnimationCallbacks.RecentsAnimationListener {
+ private static final String TAG = "AbsSwipeUpHandler";
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[16] : null;
+ protected final BaseActivityInterface<?, T> mActivityInterface;
+ protected final InputConsumerProxy mInputConsumerProxy;
+ protected final ActivityInitListener mActivityInitListener;
+ // Callbacks to be made once the recents animation starts
+ private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
+ protected RecentsAnimationController mRecentsAnimationController;
+ protected RecentsAnimationTargets mRecentsAnimationTargets;
+ protected T mActivity;
+ protected Q mRecentsView;
+ protected Runnable mGestureEndCallback;
+ protected MultiStateCallback mStateCallback;
+ protected boolean mCanceled;
+ private boolean mRecentsViewScrollLinked = false;
+
private static int getFlagForIndex(int index, String name) {
if (DEBUG_STATES) {
STATE_NAMES[index] = name;
@@ -201,11 +233,15 @@
private final Runnable mOnDeferredActivityLaunch = this::onDeferredActivityLaunch;
- public BaseSwipeUpHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
+ public AbsSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState,
long touchTimeMs, boolean continuingLastGesture,
InputConsumerController inputConsumer) {
- super(context, deviceState, gestureState, inputConsumer);
+ super(context, deviceState, gestureState, new TransformParams());
+ mActivityInterface = gestureState.getActivityInterface();
+ mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
+ mInputConsumerProxy =
+ new InputConsumerProxy(inputConsumer, this::createNewInputProxyHandler);
mTaskAnimationManager = taskAnimationManager;
mTouchTimeMs = touchTimeMs;
mContinuingLastGesture = continuingLastGesture;
@@ -274,9 +310,17 @@
}
}
- @Override
protected boolean onActivityInit(Boolean alreadyOnHome) {
- super.onActivityInit(alreadyOnHome);
+ T createdActivity = mActivityInterface.getCreatedActivity();
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
+ }
+ if (createdActivity != null) {
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
+ }
+ initTransitionEndpoints(createdActivity.getDeviceProfile());
+ }
final T activity = mActivityInterface.getCreatedActivity();
if (mActivity == activity) {
return true;
@@ -315,7 +359,9 @@
return true;
}
- @Override
+ /**
+ * Return true if the window should be translated horizontally if the recents view scrolls
+ */
protected boolean moveWindowWithRecentsScroll() {
return mGestureState.getEndTarget() != HOME;
}
@@ -394,7 +440,7 @@
mGestureState.runOnceAtState(STATE_END_TARGET_SET,
() -> mDeviceState.getRotationTouchHelper().
onEndTargetCalculated(mGestureState.getEndTarget(),
- mActivityInterface));
+ mActivityInterface));
notifyGestureStartedAsync();
}
@@ -444,7 +490,9 @@
.getHighResLoadingState().setVisible(true);
}
- @Override
+ /**
+ * Called when motion pause is detected
+ */
public void onMotionPauseChanged(boolean isPaused) {
setShelfState(isPaused ? PEEK : HIDE, ShelfPeekAnim.INTERPOLATOR, ShelfPeekAnim.DURATION);
}
@@ -482,7 +530,6 @@
mAnimationFactory.setRecentsAttachedToAppWindow(recentsAttachedToAppWindow, animate);
}
- @Override
public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) {
setIsLikelyToStartNewTask(isLikelyToStartNewTask, true /* animate */);
}
@@ -538,11 +585,14 @@
updateLauncherTransitionProgress();
}
- @Override
public Intent getLaunchIntent() {
return mGestureState.getOverviewIntent();
}
+ /**
+ * Called when the value of {@link #mCurrentShift} changes
+ */
+ @UiThread
@Override
public void updateFinalShift() {
final boolean passed = mCurrentShift.value >= MIN_PROGRESS_FOR_OVERVIEW;
@@ -603,7 +653,41 @@
public void onRecentsAnimationStart(RecentsAnimationController controller,
RecentsAnimationTargets targets) {
ActiveGestureLog.INSTANCE.addLog("startRecentsAnimationCallback", targets.apps.length);
- super.onRecentsAnimationStart(controller, targets);
+ mRecentsAnimationController = controller;
+ mRecentsAnimationTargets = targets;
+ mTransformParams.setTargetSet(mRecentsAnimationTargets);
+ RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
+ mGestureState.getRunningTaskId());
+
+ if (runningTaskTarget != null) {
+ mTaskViewSimulator.setPreview(runningTaskTarget);
+ }
+
+ // Only initialize the device profile, if it has not been initialized before, as in some
+ // configurations targets.homeContentInsets may not be correct.
+ if (mActivity == null) {
+ DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
+ if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
+ Rect overviewStackBounds = mActivityInterface
+ .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
+ dp = dp.getMultiWindowProfile(mContext,
+ new WindowBounds(overviewStackBounds, targets.homeContentInsets));
+ } else {
+ // If we are not in multi-window mode, home insets should be same as system insets.
+ dp = dp.copy(mContext);
+ }
+ dp.updateInsets(targets.homeContentInsets);
+ dp.updateIsSeascape(mContext);
+ initTransitionEndpoints(dp);
+ }
+
+ // Notify when the animation starts
+ if (!mRecentsAnimationStartCallbacks.isEmpty()) {
+ for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
+ action.run();
+ }
+ mRecentsAnimationStartCallbacks.clear();
+ }
// Only add the callback to enable the input consumer after we actually have the controller
mStateCallback.runOnceAtState(STATE_APP_CONTROLLER_RECEIVED | STATE_GESTURE_STARTED,
@@ -620,10 +704,14 @@
mStateCallback.setStateOnUiThread(STATE_GESTURE_CANCELLED | STATE_HANDLER_INVALIDATED);
// Defer clearing the controller and the targets until after we've updated the state
- super.onRecentsAnimationCanceled(thumbnailData);
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
}
- @Override
+ @UiThread
public void onGestureStarted(boolean isLikelyToStartNewTask) {
notifyGestureStartedAsync();
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
@@ -647,7 +735,7 @@
/**
* Called as a result on ACTION_CANCEL to return the UI to the start state.
*/
- @Override
+ @UiThread
public void onGestureCancelled() {
updateDisplacement(0);
mStateCallback.setStateOnUiThread(STATE_GESTURE_COMPLETED);
@@ -660,7 +748,7 @@
* @param velocity The x and y components of the velocity when the gesture ends.
* @param downPos The x and y value of where the gesture started.
*/
- @Override
+ @UiThread
public void onGestureEnded(float endVelocity, PointF velocity, PointF downPos) {
float flingThreshold = mContext.getResources()
.getDimension(R.dimen.quickstep_fling_threshold_velocity);
@@ -678,7 +766,10 @@
handleNormalGestureEnd(endVelocity, isFling, velocity, false /* isCancel */);
}
- @Override
+ /**
+ * Called to create a input proxy for the running task
+ */
+ @UiThread
protected InputConsumer createNewInputProxyHandler() {
endRunningWindowAnim(mGestureState.getEndTarget() == HOME /* cancel */);
endLauncherTransitionController();
@@ -719,7 +810,7 @@
ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget());
}
- @Override
+ /** @return Whether this was the task we were waiting to appear, and thus handled it. */
protected boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
if (mStateCallback.hasStates(STATE_HANDLER_INVALIDATED)) {
return false;
@@ -840,11 +931,9 @@
}
}
- if (endTarget.isLauncher && mRecentsAnimationController != null) {
- mRecentsAnimationController.enableInputProxy(mInputConsumer,
- this::createNewInputProxyHandler);
+ if (endTarget.isLauncher) {
+ mInputConsumerProxy.enable();
}
-
if (endTarget == HOME) {
setShelfState(ShelfAnimState.CANCEL, LINEAR, 0);
duration = Math.max(MIN_OVERSHOOT_DURATION, duration);
@@ -1099,10 +1188,12 @@
mActivityInterface.onSwipeUpToHomeComplete(mDeviceState);
}
});
+ if (mRecentsAnimationTargets != null) {
+ mRecentsAnimationTargets.addReleaseCheck(anim);
+ }
return anim;
}
- @Override
public void onConsumerAboutToBeSwitched() {
if (mActivity != null) {
// In the off chance that the gesture ends before Launcher is started, we should clear
@@ -1153,9 +1244,19 @@
});
}
- @Override
+ /**
+ * Called when we successfully startNewTask() on the task that was previously running. Normally
+ * we call resumeLastTask() when returning to the previously running task, but this handles a
+ * specific edge case: if we switch from A to B, and back to A before B appears, we need to
+ * start A again to ensure it stays on top.
+ */
+ @androidx.annotation.CallSuper
protected void onRestartPreviouslyAppearedTask() {
- super.onRestartPreviouslyAppearedTask();
+ // Finish the controller here, since we won't get onTaskAppeared() for a task that already
+ // appeared.
+ if (mRecentsAnimationController != null) {
+ mRecentsAnimationController.finish(false, null);
+ }
reset();
}
@@ -1177,6 +1278,7 @@
}
private void invalidateHandler() {
+ mInputConsumerProxy.destroy();
endRunningWindowAnim(false /* cancel */);
if (mGestureEndCallback != null) {
@@ -1259,7 +1361,7 @@
// new thumbnail
finishTransitionPosted = ViewUtils.postDraw(taskView,
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
- this::isCanceled);
+ this::isCanceled);
}
}
if (!finishTransitionPosted) {
@@ -1328,4 +1430,172 @@
return app.isNotInRecents
|| app.activityType == ACTIVITY_TYPE_HOME;
}
+
+ /**
+ * To be called at the end of constructor of subclasses. This calls various methods which can
+ * depend on proper class initialization.
+ */
+ protected void initAfterSubclassConstructor() {
+ initTransitionEndpoints(
+ mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
+ }
+
+ protected void performHapticFeedback() {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+ }
+
+ public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
+ return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
+ }
+
+ public void setGestureEndCallback(Runnable gestureEndCallback) {
+ mGestureEndCallback = gestureEndCallback;
+ }
+
+ protected void linkRecentsViewScroll() {
+ SurfaceTransactionApplier.create(mRecentsView, applier -> {
+ mTransformParams.setSyncTransactionApplier(applier);
+ runOnRecentsAnimationStart(() ->
+ mRecentsAnimationTargets.addReleaseCheck(applier));
+ });
+
+ mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
+ if (moveWindowWithRecentsScroll()) {
+ updateFinalShift();
+ }
+ });
+ runOnRecentsAnimationStart(() ->
+ mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
+ mRecentsAnimationTargets));
+ mRecentsViewScrollLinked = true;
+ }
+
+ protected void startNewTask(Consumer<Boolean> resultCallback) {
+ // Launch the task user scrolled to (mRecentsView.getNextPage()).
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ // We finish recents animation inside launchTask() when live tile is enabled.
+ mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
+ true /* freezeTaskList */);
+ } else {
+ if (!mCanceled) {
+ TaskView nextTask = mRecentsView.getNextPageTaskView();
+ if (nextTask != null) {
+ int taskId = nextTask.getTask().key.id;
+ mGestureState.updateLastStartedTaskId(taskId);
+ boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
+ .contains(taskId);
+ nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
+ success -> {
+ resultCallback.accept(success);
+ if (success) {
+ if (hasTaskPreviouslyAppeared) {
+ onRestartPreviouslyAppearedTask();
+ }
+ } else {
+ mActivityInterface.onLaunchTaskFailed();
+ nextTask.notifyTaskLaunchFailed(TAG);
+ mRecentsAnimationController.finish(true /* toRecents */, null);
+ }
+ }, MAIN_EXECUTOR.getHandler());
+ } else {
+ mActivityInterface.onLaunchTaskFailed();
+ Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
+ mRecentsAnimationController.finish(true /* toRecents */, null);
+ }
+ }
+ mCanceled = false;
+ }
+ }
+
+ /**
+ * Runs the given {@param action} if the recents animation has already started, or queues it to
+ * be run when it is next started.
+ */
+ protected void runOnRecentsAnimationStart(Runnable action) {
+ if (mRecentsAnimationTargets == null) {
+ mRecentsAnimationStartCallbacks.add(action);
+ } else {
+ action.run();
+ }
+ }
+
+ /**
+ * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
+ * @return whether the recents animation has started and there are valid app targets.
+ */
+ protected boolean hasTargets() {
+ return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
+ }
+
+ @Override
+ public void onRecentsAnimationFinished(RecentsAnimationController controller) {
+ mRecentsAnimationController = null;
+ mRecentsAnimationTargets = null;
+ if (mRecentsView != null) {
+ mRecentsView.setRecentsAnimationTargets(null, null);
+ }
+ }
+
+ @Override
+ public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
+ if (mRecentsAnimationController != null) {
+ if (handleTaskAppeared(appearedTaskTarget)) {
+ mRecentsAnimationController.finish(false /* toRecents */,
+ null /* onFinishComplete */);
+ mActivityInterface.onLaunchTaskSuccess();
+ ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
+ }
+ }
+ }
+
+ /**
+ * @return The index of the TaskView in RecentsView whose taskId matches the task that will
+ * resume if we finish the controller.
+ */
+ protected int getLastAppearedTaskIndex() {
+ return mGestureState.getLastAppearedTaskId() != -1
+ ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
+ : mRecentsView.getRunningTaskIndex();
+ }
+
+ /**
+ * @return Whether we are continuing a gesture that already landed on a new task,
+ * but before that task appeared.
+ */
+ protected boolean hasStartedNewTask() {
+ return mGestureState.getLastStartedTaskId() != -1;
+ }
+
+ /**
+ * Registers a callback to run when the activity is ready.
+ * @param intent The intent that will be used to start the activity if it doesn't exist already.
+ */
+ public void initWhenReady(Intent intent) {
+ // Preload the plan
+ RecentsModel.INSTANCE.get(mContext).getTasks(null);
+
+ mActivityInitListener.register(intent);
+ }
+
+ /**
+ * Applies the transform on the recents animation
+ */
+ protected void applyWindowTransform() {
+ if (mWindowTransitionController != null) {
+ float progress = mCurrentShift.value / mDragLengthFactor;
+ mWindowTransitionController.setPlayFraction(progress);
+ }
+ if (mRecentsAnimationTargets != null) {
+ if (mRecentsViewScrollLinked) {
+ mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
+ }
+ mTaskViewSimulator.apply(mTransformParams);
+ }
+ }
+
+ public interface Factory {
+
+ AbsSwipeUpHandler<StatefulActivity<?>, RecentsView> newHandler(
+ GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
+ }
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
deleted file mode 100644
index b49299d..0000000
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/BaseSwipeUpHandler.java
+++ /dev/null
@@ -1,393 +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.quickstep;
-
-import static android.widget.Toast.LENGTH_SHORT;
-
-import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
-import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
-import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.PointF;
-import android.graphics.Rect;
-import android.os.Build;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.widget.Toast;
-
-import androidx.annotation.CallSuper;
-import androidx.annotation.UiThread;
-
-import com.android.launcher3.DeviceProfile;
-import com.android.launcher3.R;
-import com.android.launcher3.statemanager.StatefulActivity;
-import com.android.launcher3.testing.TestProtocol;
-import com.android.launcher3.util.VibratorWrapper;
-import com.android.launcher3.util.WindowBounds;
-import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
-import com.android.quickstep.util.ActiveGestureLog;
-import com.android.quickstep.util.ActivityInitListener;
-import com.android.quickstep.util.RectFSpringAnim;
-import com.android.quickstep.util.SurfaceTransactionApplier;
-import com.android.quickstep.util.TransformParams;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.InputConsumerController;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-
-import java.util.ArrayList;
-import java.util.function.Consumer;
-
-/**
- * Base class for swipe up handler with some utility methods
- */
-@TargetApi(Build.VERSION_CODES.Q)
-public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
- extends SwipeUpAnimationLogic implements RecentsAnimationListener {
-
- private static final String TAG = "BaseSwipeUpHandler";
-
- protected final BaseActivityInterface<?, T> mActivityInterface;
- protected final InputConsumerController mInputConsumer;
-
- protected final ActivityInitListener mActivityInitListener;
-
- protected RecentsAnimationController mRecentsAnimationController;
- protected RecentsAnimationTargets mRecentsAnimationTargets;
-
- // Callbacks to be made once the recents animation starts
- private final ArrayList<Runnable> mRecentsAnimationStartCallbacks = new ArrayList<>();
-
- protected T mActivity;
- protected Q mRecentsView;
-
- protected Runnable mGestureEndCallback;
-
- protected MultiStateCallback mStateCallback;
-
- protected boolean mCanceled;
-
- private boolean mRecentsViewScrollLinked = false;
-
- protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
- GestureState gestureState, InputConsumerController inputConsumer) {
- super(context, deviceState, gestureState, new TransformParams());
- mActivityInterface = gestureState.getActivityInterface();
- mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
- mInputConsumer = inputConsumer;
- }
-
- /**
- * To be called at the end of constructor of subclasses. This calls various methods which can
- * depend on proper class initialization.
- */
- protected void initAfterSubclassConstructor() {
- initTransitionEndpoints(
- mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
- }
-
- protected void performHapticFeedback() {
- VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
- }
-
- public Consumer<MotionEvent> getRecentsViewDispatcher(float navbarRotation) {
- return mRecentsView != null ? mRecentsView.getEventDispatcher(navbarRotation) : null;
- }
-
- public void setGestureEndCallback(Runnable gestureEndCallback) {
- mGestureEndCallback = gestureEndCallback;
- }
-
- public abstract Intent getLaunchIntent();
-
- protected void linkRecentsViewScroll() {
- SurfaceTransactionApplier.create(mRecentsView, applier -> {
- mTransformParams.setSyncTransactionApplier(applier);
- runOnRecentsAnimationStart(() ->
- mRecentsAnimationTargets.addReleaseCheck(applier));
- });
-
- mRecentsView.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
- if (moveWindowWithRecentsScroll()) {
- updateFinalShift();
- }
- });
- runOnRecentsAnimationStart(() ->
- mRecentsView.setRecentsAnimationTargets(mRecentsAnimationController,
- mRecentsAnimationTargets));
- mRecentsViewScrollLinked = true;
- }
-
- protected void startNewTask(Consumer<Boolean> resultCallback) {
- // Launch the task user scrolled to (mRecentsView.getNextPage()).
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- // We finish recents animation inside launchTask() when live tile is enabled.
- mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
- true /* freezeTaskList */);
- } else {
- if (!mCanceled) {
- TaskView nextTask = mRecentsView.getNextPageTaskView();
- if (nextTask != null) {
- int taskId = nextTask.getTask().key.id;
- mGestureState.updateLastStartedTaskId(taskId);
- boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
- .contains(taskId);
- nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
- success -> {
- resultCallback.accept(success);
- if (success) {
- if (hasTaskPreviouslyAppeared) {
- onRestartPreviouslyAppearedTask();
- }
- } else {
- mActivityInterface.onLaunchTaskFailed();
- nextTask.notifyTaskLaunchFailed(TAG);
- mRecentsAnimationController.finish(true /* toRecents */, null);
- }
- }, MAIN_EXECUTOR.getHandler());
- } else {
- mActivityInterface.onLaunchTaskFailed();
- Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
- mRecentsAnimationController.finish(true /* toRecents */, null);
- }
- }
- mCanceled = false;
- }
- }
-
- /**
- * Called when we successfully startNewTask() on the task that was previously running. Normally
- * we call resumeLastTask() when returning to the previously running task, but this handles a
- * specific edge case: if we switch from A to B, and back to A before B appears, we need to
- * start A again to ensure it stays on top.
- */
- @CallSuper
- protected void onRestartPreviouslyAppearedTask() {
- // Finish the controller here, since we won't get onTaskAppeared() for a task that already
- // appeared.
- if (mRecentsAnimationController != null) {
- mRecentsAnimationController.finish(false, null);
- }
- }
-
- /**
- * Runs the given {@param action} if the recents animation has already started, or queues it to
- * be run when it is next started.
- */
- protected void runOnRecentsAnimationStart(Runnable action) {
- if (mRecentsAnimationTargets == null) {
- mRecentsAnimationStartCallbacks.add(action);
- } else {
- action.run();
- }
- }
-
- /**
- * TODO can we remove this now that we don't finish the controller until onTaskAppeared()?
- * @return whether the recents animation has started and there are valid app targets.
- */
- protected boolean hasTargets() {
- return mRecentsAnimationTargets != null && mRecentsAnimationTargets.hasTargets();
- }
-
- @Override
- public void onRecentsAnimationStart(RecentsAnimationController recentsAnimationController,
- RecentsAnimationTargets targets) {
- mRecentsAnimationController = recentsAnimationController;
- mRecentsAnimationTargets = targets;
- mTransformParams.setTargetSet(mRecentsAnimationTargets);
- RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
- mGestureState.getRunningTaskId());
-
- if (runningTaskTarget != null) {
- mTaskViewSimulator.setPreview(runningTaskTarget);
- }
-
- // Only initialize the device profile, if it has not been initialized before, as in some
- // configurations targets.homeContentInsets may not be correct.
- if (mActivity == null) {
- DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
- if (targets.minimizedHomeBounds != null && runningTaskTarget != null) {
- Rect overviewStackBounds = mActivityInterface
- .getOverviewWindowBounds(targets.minimizedHomeBounds, runningTaskTarget);
- dp = dp.getMultiWindowProfile(mContext,
- new WindowBounds(overviewStackBounds, targets.homeContentInsets));
- } else {
- // If we are not in multi-window mode, home insets should be same as system insets.
- dp = dp.copy(mContext);
- }
- dp.updateInsets(targets.homeContentInsets);
- dp.updateIsSeascape(mContext);
- initTransitionEndpoints(dp);
- }
-
- // Notify when the animation starts
- if (!mRecentsAnimationStartCallbacks.isEmpty()) {
- for (Runnable action : new ArrayList<>(mRecentsAnimationStartCallbacks)) {
- action.run();
- }
- mRecentsAnimationStartCallbacks.clear();
- }
- }
-
- @Override
- public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
- mRecentsAnimationController = null;
- mRecentsAnimationTargets = null;
- if (mRecentsView != null) {
- mRecentsView.setRecentsAnimationTargets(null, null);
- }
- }
-
- @Override
- public void onRecentsAnimationFinished(RecentsAnimationController controller) {
- mRecentsAnimationController = null;
- mRecentsAnimationTargets = null;
- if (mRecentsView != null) {
- mRecentsView.setRecentsAnimationTargets(null, null);
- }
- }
-
- @Override
- public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
- if (mRecentsAnimationController != null) {
- if (handleTaskAppeared(appearedTaskTarget)) {
- mRecentsAnimationController.finish(false /* toRecents */,
- null /* onFinishComplete */);
- mActivityInterface.onLaunchTaskSuccess();
- ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
- }
- }
- }
-
- /** @return Whether this was the task we were waiting to appear, and thus handled it. */
- protected abstract boolean handleTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget);
-
- /**
- * @return The index of the TaskView in RecentsView whose taskId matches the task that will
- * resume if we finish the controller.
- */
- protected int getLastAppearedTaskIndex() {
- return mGestureState.getLastAppearedTaskId() != -1
- ? mRecentsView.getTaskIndexForId(mGestureState.getLastAppearedTaskId())
- : mRecentsView.getRunningTaskIndex();
- }
-
- /**
- * @return Whether we are continuing a gesture that already landed on a new task,
- * but before that task appeared.
- */
- protected boolean hasStartedNewTask() {
- return mGestureState.getLastStartedTaskId() != -1;
- }
-
- /**
- * Return true if the window should be translated horizontally if the recents view scrolls
- */
- protected abstract boolean moveWindowWithRecentsScroll();
-
- protected boolean onActivityInit(Boolean alreadyOnHome) {
- T createdActivity = mActivityInterface.getCreatedActivity();
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
- }
- if (createdActivity != null) {
- if (TestProtocol.sDebugTracing) {
- Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
- }
- initTransitionEndpoints(createdActivity.getDeviceProfile());
- }
- return true;
- }
-
- /**
- * Called to create a input proxy for the running task
- */
- @UiThread
- protected abstract InputConsumer createNewInputProxyHandler();
-
- /**
- * Called when the value of {@link #mCurrentShift} changes
- */
- @UiThread
- public abstract void updateFinalShift();
-
- /**
- * Called when motion pause is detected
- */
- public abstract void onMotionPauseChanged(boolean isPaused);
-
- @UiThread
- public void onGestureStarted(boolean isLikelyToStartNewTask) { }
-
- @UiThread
- public abstract void onGestureCancelled();
-
- @UiThread
- public abstract void onGestureEnded(float endVelocity, PointF velocity, PointF downPos);
-
- public abstract void onConsumerAboutToBeSwitched();
-
- public void setIsLikelyToStartNewTask(boolean isLikelyToStartNewTask) { }
-
- /**
- * Registers a callback to run when the activity is ready.
- * @param intent The intent that will be used to start the activity if it doesn't exist already.
- */
- public void initWhenReady(Intent intent) {
- // Preload the plan
- RecentsModel.INSTANCE.get(mContext).getTasks(null);
-
- mActivityInitListener.register(intent);
- }
-
- /**
- * Applies the transform on the recents animation
- */
- protected void applyWindowTransform() {
- if (mWindowTransitionController != null) {
- float progress = mCurrentShift.value / mDragLengthFactor;
- mWindowTransitionController.setPlayFraction(progress);
- }
- if (mRecentsAnimationTargets != null) {
- if (mRecentsViewScrollLinked) {
- mTaskViewSimulator.setScroll(mRecentsView.getScrollOffset());
- }
- mTaskViewSimulator.apply(mTransformParams);
- }
- }
-
- @Override
- protected RectFSpringAnim createWindowAnimationToHome(float startProgress,
- HomeAnimationFactory homeAnimationFactory) {
- RectFSpringAnim anim =
- super.createWindowAnimationToHome(startProgress, homeAnimationFactory);
- if (mRecentsAnimationTargets != null) {
- mRecentsAnimationTargets.addReleaseCheck(anim);
- }
- return anim;
- }
-
- public interface Factory {
-
- BaseSwipeUpHandler newHandler(
- GestureState gestureState, long touchTimeMs, boolean continuingLastGesture);
- }
-}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
index f60a50b..ffb05df 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackSwipeHandler.java
@@ -71,7 +71,7 @@
*/
@TargetApi(Build.VERSION_CODES.R)
public class FallbackSwipeHandler extends
- BaseSwipeUpHandlerV2<RecentsActivity, FallbackRecentsView> {
+ AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView> {
/**
* Message used for receiving gesture nav contract information. We use a static messenger to
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
index 052d0a6..4411455 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/LauncherSwipeHandlerV2.java
@@ -39,7 +39,7 @@
* Temporary class to allow easier refactoring
*/
public class LauncherSwipeHandlerV2 extends
- BaseSwipeUpHandlerV2<BaseQuickstepLauncher, RecentsView> {
+ AbsSwipeUpHandler<BaseQuickstepLauncher, RecentsView> {
public LauncherSwipeHandlerV2(Context context, RecentsAnimationDeviceState deviceState,
TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs,
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 e5852be..1012ab2 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TouchInteractionService.java
@@ -269,9 +269,9 @@
return sIsInitialized;
}
- private final BaseSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
+ private final AbsSwipeUpHandler.Factory mLauncherSwipeHandlerFactory =
this::createLauncherSwipeHandler;
- private final BaseSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
+ private final AbsSwipeUpHandler.Factory mFallbackSwipeHandlerFactory =
this::createFallbackSwipeHandler;
private ActivityManagerWrapper mAM;
@@ -716,7 +716,7 @@
private InputConsumer createOtherActivityInputConsumer(GestureState gestureState,
MotionEvent event) {
- final BaseSwipeUpHandler.Factory factory;
+ final AbsSwipeUpHandler.Factory factory;
if (!mOverviewComponentObserver.isHomeAndOverviewSame()) {
factory = mFallbackSwipeHandlerFactory;
} else {
@@ -889,13 +889,13 @@
}
}
- private BaseSwipeUpHandler createLauncherSwipeHandler(
+ private AbsSwipeUpHandler createLauncherSwipeHandler(
GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
return new LauncherSwipeHandlerV2(this, mDeviceState, mTaskAnimationManager,
gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
}
- private BaseSwipeUpHandler createFallbackSwipeHandler(
+ private AbsSwipeUpHandler createFallbackSwipeHandler(
GestureState gestureState, long touchTimeMs, boolean continuingLastGesture) {
return new FallbackSwipeHandler(this, mDeviceState, mTaskAnimationManager,
gestureState, touchTimeMs, continuingLastGesture, mInputConsumer);
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 a676390..db1948b 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
@@ -21,7 +21,7 @@
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.MIN_PROGRESS_FOR_OVERVIEW;
+import static com.android.quickstep.AbsSwipeUpHandler.MIN_PROGRESS_FOR_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 6259f1f..f02e5e6 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -53,8 +53,8 @@
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.BaseActivityInterface;
-import com.android.quickstep.BaseSwipeUpHandler;
-import com.android.quickstep.BaseSwipeUpHandler.Factory;
+import com.android.quickstep.AbsSwipeUpHandler;
+import com.android.quickstep.AbsSwipeUpHandler.Factory;
import com.android.quickstep.GestureState;
import com.android.quickstep.InputConsumer;
import com.android.quickstep.RecentsAnimationCallbacks;
@@ -93,7 +93,7 @@
private final InputMonitorCompat mInputMonitorCompat;
private final BaseActivityInterface mActivityInterface;
- private final BaseSwipeUpHandler.Factory mHandlerFactory;
+ private final AbsSwipeUpHandler.Factory mHandlerFactory;
private final Consumer<OtherActivityInputConsumer> mOnCompleteCallback;
private final MotionPauseDetector mMotionPauseDetector;
@@ -101,7 +101,7 @@
private VelocityTracker mVelocityTracker;
- private BaseSwipeUpHandler mInteractionHandler;
+ private AbsSwipeUpHandler mInteractionHandler;
private final boolean mIsDeferredDownTarget;
private final PointF mDownPos = new PointF();
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java
new file mode 100644
index 0000000..3e87f48
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/InputConsumerProxy.java
@@ -0,0 +1,118 @@
+/*
+ * 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.MotionEvent.ACTION_CANCEL;
+import static android.view.MotionEvent.ACTION_DOWN;
+import static android.view.MotionEvent.ACTION_UP;
+
+import android.util.Log;
+import android.view.InputEvent;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import com.android.quickstep.InputConsumer;
+import com.android.systemui.shared.system.InputConsumerController;
+
+import java.util.function.Supplier;
+
+/**
+ * Utility class which manages proxying input events from {@link InputConsumerController}
+ * to an {@link InputConsumer}
+ */
+public class InputConsumerProxy {
+
+ private static final String TAG = "InputConsumerProxy";
+
+ private final InputConsumerController mInputConsumerController;
+ private final Supplier<InputConsumer> mConsumerSupplier;
+
+ // The consumer is created lazily on demand.
+ private InputConsumer mInputConsumer;
+
+ private boolean mDestroyed = false;
+ private boolean mTouchInProgress = false;
+ private boolean mDestroyPending = false;
+
+ public InputConsumerProxy(InputConsumerController inputConsumerController,
+ Supplier<InputConsumer> consumerSupplier) {
+ mInputConsumerController = inputConsumerController;
+ mConsumerSupplier = consumerSupplier;
+ }
+
+ public void enable() {
+ if (mDestroyed) {
+ return;
+ }
+ mInputConsumerController.setInputListener(this::onInputConsumerEvent);
+ }
+
+ private boolean onInputConsumerEvent(InputEvent ev) {
+ if (ev instanceof MotionEvent) {
+ onInputConsumerMotionEvent((MotionEvent) ev);
+ } else if (ev instanceof KeyEvent) {
+ if (mInputConsumer == null) {
+ mInputConsumer = mConsumerSupplier.get();
+ }
+ mInputConsumer.onKeyEvent((KeyEvent) ev);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean onInputConsumerMotionEvent(MotionEvent ev) {
+ int action = ev.getAction();
+
+ // Just to be safe, verify that ACTION_DOWN comes before any other action,
+ // and ignore any ACTION_DOWN after the first one (though that should not happen).
+ if (!mTouchInProgress && action != ACTION_DOWN) {
+ Log.w(TAG, "Received non-down motion before down motion: " + action);
+ return false;
+ }
+ if (mTouchInProgress && action == ACTION_DOWN) {
+ Log.w(TAG, "Received down motion while touch was already in progress");
+ return false;
+ }
+
+ if (action == ACTION_DOWN) {
+ mTouchInProgress = true;
+ if (mInputConsumer == null) {
+ mInputConsumer = mConsumerSupplier.get();
+ }
+ } else if (action == ACTION_CANCEL || action == ACTION_UP) {
+ // Finish any pending actions
+ mTouchInProgress = false;
+ if (mDestroyPending) {
+ destroy();
+ }
+ }
+ if (mInputConsumer != null) {
+ mInputConsumer.onMotionEvent(ev);
+ }
+
+ return true;
+ }
+
+ public void destroy() {
+ if (mTouchInProgress) {
+ mDestroyPending = true;
+ return;
+ }
+ mDestroyPending = false;
+ mDestroyed = true;
+ mInputConsumerController.setInputListener(null);
+ }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
index 2805299..2de4036 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/util/TaskViewSimulator.java
@@ -73,6 +73,7 @@
private final BaseActivityInterface mSizeStrategy;
private final Rect mTaskRect = new Rect();
+ private float mOffsetY;
private final PointF mPivot = new PointF();
private DeviceProfile mDp;
@@ -178,8 +179,12 @@
}
}
+ public void setOffsetY(float offsetY) {
+ mOffsetY = offsetY;
+ }
+
/**
- * Adds animation for all the components corresponding to transition from an app to overview
+ * Adds animation for all the components corresponding to transition from an app to overview.
*/
public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
@@ -187,6 +192,14 @@
}
/**
+ * Adds animation for all the components corresponding to transition from overview to the app.
+ */
+ public void addOverviewToAppAnim(PendingAnimation pa, TimeInterpolator interpolator) {
+ pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 0, 1, interpolator);
+ pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, 1, getFullScreenScale(), interpolator);
+ }
+
+ /**
* Returns the current clipped/visible window bounds in the window coordinate space
*/
public RectF getCurrentCropRect() {
@@ -281,7 +294,7 @@
mMatrix.postScale(scale, scale);
// Apply TaskView matrix: translate, scale, scroll
- mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
+ mMatrix.postTranslate(mTaskRect.left, mTaskRect.top + mOffsetY);
mMatrix.postScale(mCurveScale, mCurveScale, taskWidth / 2, taskHeight / 2);
mOrientationState.getOrientationHandler().set(
mMatrix, MATRIX_POST_TRANSLATE, mScrollState.scroll);
@@ -307,7 +320,7 @@
.withCornerRadius(getCurrentCornerRadius());
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
- builder.withRelativeLayerTo(params.getRecentsSurface(), Integer.MAX_VALUE);
+ builder.withRelativeLayerTo(params.getRecentsSurface(), Integer.MIN_VALUE);
}
}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
index 1034234..0a4cec7 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/views/LauncherRecentsView.java
@@ -15,7 +15,6 @@
*/
package com.android.quickstep.views;
-import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
@@ -46,7 +45,6 @@
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.LauncherActivityInterface;
import com.android.quickstep.SysUINavigationMode;
-import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.RecentsExtraCard;
@@ -57,8 +55,6 @@
public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
implements StateListener<LauncherState> {
- private final TransformParams mTransformParams = new TransformParams();
-
private RecentsExtraCard mRecentsExtraCardPlugin;
private RecentsExtraViewContainer mRecentsExtraViewContainer;
private PluginListener<RecentsExtraCard> mRecentsExtraCardPluginListener =
@@ -108,17 +104,6 @@
}
}
- @Override
- public void setTranslationY(float translationY) {
- super.setTranslationY(translationY);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- LauncherState state = mActivity.getStateManager().getState();
- if (state == OVERVIEW || state == ALL_APPS) {
- redrawLiveTile(false);
- }
- }
- }
-
/**
* Animates adjacent tasks and translate hotseat off screen as well.
*/
@@ -151,19 +136,6 @@
}
@Override
- protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
- if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
- if (tv.isRunningTask()) {
- mTransformParams.setProgress(1 - progress)
- .setSyncTransactionApplier(mSyncTransactionApplier);
- // TODO: Revisit live tiles
- } else {
- redrawLiveTile(true);
- }
- }
- }
-
- @Override
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
mActivity.getStateManager().goToState(NORMAL, false /* animate */);
@@ -175,46 +147,6 @@
}
@Override
- public void scrollTo(int x, int y) {
- super.scrollTo(x, y);
- if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile) {
- redrawLiveTile(true);
- }
- }
-
- @Override
- public TransformParams getLiveTileParams(
- boolean mightNeedToRefill) {
- if (!mEnableDrawingLiveTile || mRecentsAnimationController == null
- || mRecentsAnimationTargets == null) {
- return null;
- }
- TaskView taskView = getRunningTaskView();
- if (taskView != null) {
- taskView.getThumbnail().getGlobalVisibleRect(mTempRect);
- int offsetX = (int) (mTaskWidth * taskView.getScaleX() * getScaleX()
- - mTempRect.width());
- int offsetY = (int) (mTaskHeight * taskView.getScaleY() * getScaleY()
- - mTempRect.height());
- if (((mCurrentPage != 0) || mightNeedToRefill) && offsetX > 0) {
- if (mTempRect.left - offsetX < 0) {
- mTempRect.left -= offsetX;
- } else {
- mTempRect.right += offsetX;
- }
- }
- if (mightNeedToRefill && offsetY > 0) {
- mTempRect.top -= offsetY;
- }
- mTransformParams.setProgress(1f)
- .setTargetAlpha(taskView.getAlpha())
- .setSyncTransactionApplier(mSyncTransactionApplier)
- .setTargetSet(mRecentsAnimationTargets);
- }
- return mTransformParams;
- }
-
- @Override
public void reset() {
super.reset();
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 211a2ce..b478161 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
@@ -78,6 +78,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -129,6 +130,7 @@
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.SplitScreenBounds;
import com.android.quickstep.util.SurfaceTransactionApplier;
+import com.android.quickstep.util.TaskViewSimulator;
import com.android.quickstep.util.TransformParams;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
@@ -212,10 +214,11 @@
protected RecentsOrientedState mOrientationState;
protected final BaseActivityInterface mSizeStrategy;
protected RecentsAnimationController mRecentsAnimationController;
- protected RecentsAnimationTargets mRecentsAnimationTargets;
protected SurfaceTransactionApplier mSyncTransactionApplier;
protected int mTaskWidth;
protected int mTaskHeight;
+ protected final TransformParams mLiveTileParams = new TransformParams();
+ protected final TaskViewSimulator mLiveTileTaskViewSimulator;
protected boolean mEnableDrawingLiveTile = false;
protected final Rect mTempRect = new Rect();
private final PointF mTempPointF = new PointF();
@@ -393,7 +396,8 @@
mActivity = BaseActivity.fromContext(context);
mOrientationState = new RecentsOrientedState(
context, mSizeStrategy, this::animateRecentsRotationInPlace);
- mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation());
+ final int rotation = mActivity.getDisplay().getRotation();
+ mOrientationState.setRecentsRotation(rotation);
mFastFlingVelocity = getResources()
.getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
@@ -429,6 +433,12 @@
// Initialize quickstep specific cache params here, as this is constructed only once
mActivity.getViewCache().setCacheSize(R.layout.digital_wellbeing_toast, 5);
+
+ mLiveTileTaskViewSimulator = new TaskViewSimulator(getContext(), getSizeStrategy());
+ mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
+ mLiveTileTaskViewSimulator.setLayoutRotation(getPagedViewOrientedState().getTouchRotation(),
+ getPagedViewOrientedState().getDisplayRotation());
+ mLiveTileTaskViewSimulator.setRecentsRotation(rotation);
}
public OverScroller getScroller() {
@@ -513,6 +523,7 @@
mActivity.addMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = new SurfaceTransactionApplier(this);
+ mLiveTileParams.setSyncTransactionApplier(mSyncTransactionApplier);
RecentsModel.INSTANCE.get(getContext()).addThumbnailChangeListener(this);
mIdp.addOnChangeListener(this);
mIPinnedStackAnimationListener.setActivity(mActivity);
@@ -530,6 +541,7 @@
mActivity.removeMultiWindowModeChangedListener(mMultiWindowModeChangedListener);
ActivityManagerWrapper.getInstance().unregisterTaskStackListener(mTaskStackListener);
mSyncTransactionApplier = null;
+ mLiveTileParams.setSyncTransactionApplier(null);
RecentsModel.INSTANCE.get(getContext()).removeThumbnailChangeListener(this);
mIdp.removeOnChangeListener(this);
SystemUiProxy.INSTANCE.get(getContext()).setPinnedStackAnimationListener(null);
@@ -648,6 +660,16 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
+
+ TaskView taskView = getCurrentPageTaskView();
+ if (taskView != null) {
+ TouchDelegate mChildTouchDelegate = taskView.getIconTouchDelegate(ev);
+ if (mChildTouchDelegate != null && mChildTouchDelegate.onTouchEvent(ev)) {
+ // Keep consuming events to pass to delegate
+ return true;
+ }
+ }
+
final int x = (int) ev.getX();
final int y = (int) ev.getY();
switch (ev.getAction()) {
@@ -847,6 +869,7 @@
public void setInsets(Rect insets) {
mInsets.set(insets);
resetPaddingFromTaskSize();
+ mLiveTileTaskViewSimulator.setDp(mActivity.getDeviceProfile());
}
private void resetPaddingFromTaskSize() {
@@ -888,6 +911,12 @@
// Update the high res thumbnail loader state
mModel.getThumbnailCache().getHighResLoadingState().setFlingingFast(isFlingingFast);
+
+ mLiveTileTaskViewSimulator.setScroll(getScrollOffset());
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mEnableDrawingLiveTile
+ && mLiveTileParams.getTargetSet() != null) {
+ redrawLiveTile();
+ }
return scrolling;
}
@@ -995,7 +1024,7 @@
mTaskListChangeId = -1;
mRecentsAnimationController = null;
- mRecentsAnimationTargets = null;
+ mLiveTileParams.setTargetSet(null);
unloadVisibleTaskData();
setCurrentPage(0);
@@ -1657,9 +1686,11 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
- if (mOrientationState.setRecentsRotation(mActivity.getDisplay().getRotation())) {
+ final int rotation = mActivity.getDisplay().getRotation();
+ if (mOrientationState.setRecentsRotation(rotation)) {
updateOrientationHandler();
}
+ mLiveTileTaskViewSimulator.setRecentsRotation(rotation);
}
public void setLayoutRotation(int touchRotation, int displayRotation) {
@@ -1688,6 +1719,8 @@
requestLayout();
// Reapply the current page to update page scrolls.
setCurrentPage(mCurrentPage);
+ mLiveTileTaskViewSimulator.setLayoutRotation(getPagedViewOrientedState().getTouchRotation(),
+ getPagedViewOrientedState().getDisplayRotation());
}
public RecentsOrientedState getPagedViewOrientedState() {
@@ -1945,8 +1978,6 @@
? targetSysUiFlags
: 0);
- onTaskLaunchAnimationUpdate(animator.getAnimatedFraction(), tv);
-
// Passing the threshold from taskview to fullscreen app will vibrate
final boolean passed = animator.getAnimatedFraction() >=
SUCCESS_TRANSITION_PROGRESS;
@@ -1970,6 +2001,10 @@
mPendingAnimation = new PendingAnimation(duration);
mPendingAnimation.add(anim);
+ if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
+ mLiveTileTaskViewSimulator.addOverviewToAppAnim(mPendingAnimation, interpolator);
+ mPendingAnimation.addOnFrameCallback(this::redrawLiveTile);
+ }
mPendingAnimation.addEndListener((endState) -> {
if (endState.isSuccess) {
Consumer<Boolean> onLaunchResult = (result) -> {
@@ -1995,9 +2030,6 @@
return mPendingAnimation;
}
- protected void onTaskLaunchAnimationUpdate(float progress, TaskView tv) {
- }
-
protected void onTaskLaunchAnimationEnd(boolean success) {
if (success) {
resetTaskVisuals();
@@ -2064,13 +2096,23 @@
mEnableDrawingLiveTile = enableDrawingLiveTile;
}
- public void redrawLiveTile(boolean mightNeedToRefill) { }
+ public void redrawLiveTile() {
+ mLiveTileTaskViewSimulator.apply(mLiveTileParams);
+ }
+
+ public TaskViewSimulator getLiveTileTaskViewSimulator() {
+ return mLiveTileTaskViewSimulator;
+ }
// TODO: To be removed in a follow up CL
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets recentsAnimationTargets) {
mRecentsAnimationController = recentsAnimationController;
- mRecentsAnimationTargets = recentsAnimationTargets;
+ if (recentsAnimationTargets != null) {
+ mLiveTileTaskViewSimulator.setPreview(
+ recentsAnimationTargets.apps[recentsAnimationTargets.apps.length - 1]);
+ mLiveTileParams.setTargetSet(recentsAnimationTargets);
+ }
}
public void setLiveTileOverlayAttached(boolean liveTileOverlayAttached) {
@@ -2206,11 +2248,6 @@
};
}
- public TransformParams getLiveTileParams(
- boolean mightNeedToRefill) {
- return null;
- }
-
private void updateEnabledOverlays() {
int overlayEnabledPage = mOverlayEnabled ? getNextPage() : -1;
int taskCount = getTaskViewCount();
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 82fabac..27d84e6 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
@@ -22,10 +22,14 @@
import static android.view.Gravity.END;
import static android.view.Gravity.START;
import static android.view.Gravity.TOP;
+import static android.view.Surface.ROTATION_180;
+import static android.view.Surface.ROTATION_270;
+import static android.view.Surface.ROTATION_90;
import static android.widget.Toast.LENGTH_SHORT;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.comp;
+import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
@@ -52,7 +56,9 @@
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
+import android.view.MotionEvent;
import android.view.Surface;
+import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewOutlineProvider;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -77,6 +83,7 @@
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.ComponentKey;
+import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
@@ -121,6 +128,13 @@
public static final long SCALE_ICON_DURATION = 120;
private static final long DIM_ANIM_DURATION = 700;
+ /**
+ * This technically can be a vanilla {@link TouchDelegate} class, however that class requires
+ * setting the touch bounds at construction, so we'd repeatedly be created many instances
+ * unnecessarily as scrolling occurs, whereas {@link TransformingTouchDelegate} allows touch
+ * delegated bounds only to be updated.
+ */
+ private TransformingTouchDelegate mIconTouchDelegate;
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
@@ -185,6 +199,7 @@
private int mStackHeight;
private View mContextualChipWrapper;
private View mContextualChip;
+ private final float[] mIconCenterCoords = new float[2];
public TaskView(Context context) {
this(context, null);
@@ -245,6 +260,26 @@
super.onFinishInflate();
mSnapshotView = findViewById(R.id.snapshot);
mIconView = findViewById(R.id.icon);
+ mIconTouchDelegate = new TransformingTouchDelegate(mIconView);
+ }
+
+ public TouchDelegate getIconTouchDelegate(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ computeAndSetIconTouchDelegate();
+ }
+ return mIconTouchDelegate;
+ }
+
+ private void computeAndSetIconTouchDelegate() {
+ float iconHalfSize = mIconView.getWidth() / 2f;
+ mIconCenterCoords[0] = mIconCenterCoords[1] = iconHalfSize;
+ getDescendantCoordRelativeToAncestor(mIconView, mActivity.getDragLayer(), mIconCenterCoords,
+ false);
+ mIconTouchDelegate.setBounds(
+ (int) (mIconCenterCoords[0] - iconHalfSize),
+ (int) (mIconCenterCoords[1] - iconHalfSize),
+ (int) (mIconCenterCoords[0] + iconHalfSize),
+ (int) (mIconCenterCoords[1] + iconHalfSize));
}
/**
@@ -466,18 +501,18 @@
int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
switch (orientationHandler.getRotation()) {
- case Surface.ROTATION_90:
+ case ROTATION_90:
iconParams.gravity = (isRtl ? START : END) | CENTER_VERTICAL;
iconParams.rightMargin = -thumbnailPadding;
iconParams.leftMargin = 0;
iconParams.topMargin = snapshotParams.topMargin / 2;
break;
- case Surface.ROTATION_180:
+ case ROTATION_180:
iconParams.gravity = BOTTOM | CENTER_HORIZONTAL;
iconParams.bottomMargin = -thumbnailPadding;
iconParams.leftMargin = iconParams.topMargin = iconParams.rightMargin = 0;
break;
- case Surface.ROTATION_270:
+ case ROTATION_270:
iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
iconParams.leftMargin = -thumbnailPadding;
iconParams.rightMargin = 0;
diff --git a/quickstep/res/layout/overview_actions_container.xml b/quickstep/res/layout/overview_actions_container.xml
index e05688e..258f24a 100644
--- a/quickstep/res/layout/overview_actions_container.xml
+++ b/quickstep/res/layout/overview_actions_container.xml
@@ -17,9 +17,7 @@
<com.android.quickstep.views.OverviewActionsView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/overview_actions_height"
- android:layout_gravity="center_horizontal|bottom"
- android:layout_marginLeft="@dimen/overview_actions_horizontal_margin"
- android:layout_marginRight="@dimen/overview_actions_horizontal_margin">
+ android:layout_gravity="center_horizontal|bottom">
<LinearLayout
android:id="@+id/action_buttons"
diff --git a/quickstep/src/com/android/quickstep/BaseActivityInterface.java b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
index f4a394a..0ae386f 100644
--- a/quickstep/src/com/android/quickstep/BaseActivityInterface.java
+++ b/quickstep/src/com/android/quickstep/BaseActivityInterface.java
@@ -20,7 +20,7 @@
import static com.android.launcher3.anim.Interpolators.INSTANT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.RECENTS_ATTACH_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.RECENTS_ATTACH_DURATION;
import static com.android.quickstep.SysUINavigationMode.getMode;
import static com.android.quickstep.SysUINavigationMode.hideShelfInTwoButtonLandscape;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationController.java b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
index 4e9aa61..51f5e5d 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationController.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationController.java
@@ -15,49 +15,30 @@
*/
package com.android.quickstep;
-import static android.view.MotionEvent.ACTION_CANCEL;
-import static android.view.MotionEvent.ACTION_DOWN;
-import static android.view.MotionEvent.ACTION_UP;
-
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
-import android.os.SystemClock;
-import android.util.Log;
-import android.view.InputEvent;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
-import java.util.function.Supplier;
/**
* Wrapper around RecentsAnimationControllerCompat to help with some synchronization
*/
public class RecentsAnimationController {
- private static final String TAG = "RecentsAnimationController";
-
private final RecentsAnimationControllerCompat mController;
private final Consumer<RecentsAnimationController> mOnFinishedListener;
private final boolean mAllowMinimizeSplitScreen;
- private InputConsumerController mInputConsumerController;
- private Supplier<InputConsumer> mInputProxySupplier;
- private InputConsumer mInputConsumer;
private boolean mUseLauncherSysBarFlags = false;
private boolean mSplitScreenMinimized = false;
- private boolean mTouchInProgress;
- private boolean mDisableInputProxyPending;
public RecentsAnimationController(RecentsAnimationControllerCompat controller,
boolean allowMinimizeSplitScreen,
@@ -136,12 +117,12 @@
@UiThread
public void finishAnimationToHome() {
- finishAndDisableInputProxy(true /* toRecents */, null, false /* sendUserLeaveHint */);
+ finishController(true /* toRecents */, null, false /* sendUserLeaveHint */);
}
@UiThread
public void finishAnimationToApp() {
- finishAndDisableInputProxy(false /* toRecents */, null, false /* sendUserLeaveHint */);
+ finishController(false /* toRecents */, null, false /* sendUserLeaveHint */);
}
/** See {@link #finish(boolean, Runnable, boolean)} */
@@ -160,18 +141,6 @@
@UiThread
public void finish(boolean toRecents, Runnable onFinishComplete, boolean sendUserLeaveHint) {
Preconditions.assertUIThread();
- if (toRecents && mTouchInProgress) {
- // Finish the controller as requested, but don't disable input proxy yet.
- mDisableInputProxyPending = true;
- finishController(toRecents, onFinishComplete, sendUserLeaveHint);
- } else {
- finishAndDisableInputProxy(toRecents, onFinishComplete, sendUserLeaveHint);
- }
- }
-
- private void finishAndDisableInputProxy(boolean toRecents, Runnable onFinishComplete,
- boolean sendUserLeaveHint) {
- disableInputProxy();
finishController(toRecents, onFinishComplete, sendUserLeaveHint);
}
@@ -179,7 +148,6 @@
public void finishController(boolean toRecents, Runnable callback, boolean sendUserLeaveHint) {
mOnFinishedListener.accept(this);
UI_HELPER_EXECUTOR.execute(() -> {
- mController.setInputConsumerEnabled(false);
mController.finish(toRecents, sendUserLeaveHint);
if (callback != null) {
MAIN_EXECUTOR.execute(callback);
@@ -197,75 +165,8 @@
});
}
- public void enableInputProxy(InputConsumerController inputConsumerController,
- Supplier<InputConsumer> inputProxySupplier) {
- mInputProxySupplier = inputProxySupplier;
- mInputConsumerController = inputConsumerController;
- mInputConsumerController.setInputListener(this::onInputConsumerEvent);
- }
-
/** @return wrapper controller. */
public RecentsAnimationControllerCompat getController() {
return mController;
}
-
- private void disableInputProxy() {
- if (mInputConsumer != null && mTouchInProgress) {
- long now = SystemClock.uptimeMillis();
- MotionEvent dummyCancel = MotionEvent.obtain(now, now, ACTION_CANCEL, 0, 0, 0);
- mInputConsumer.onMotionEvent(dummyCancel);
- dummyCancel.recycle();
- }
- if (mInputConsumerController != null) {
- mInputConsumerController.setInputListener(null);
- }
- mInputProxySupplier = null;
- }
-
- private boolean onInputConsumerEvent(InputEvent ev) {
- if (ev instanceof MotionEvent) {
- onInputConsumerMotionEvent((MotionEvent) ev);
- } else if (ev instanceof KeyEvent) {
- if (mInputConsumer == null) {
- mInputConsumer = mInputProxySupplier.get();
- }
- mInputConsumer.onKeyEvent((KeyEvent) ev);
- return true;
- }
- return false;
- }
-
- private boolean onInputConsumerMotionEvent(MotionEvent ev) {
- int action = ev.getAction();
-
- // Just to be safe, verify that ACTION_DOWN comes before any other action,
- // and ignore any ACTION_DOWN after the first one (though that should not happen).
- if (!mTouchInProgress && action != ACTION_DOWN) {
- Log.w(TAG, "Received non-down motion before down motion: " + action);
- return false;
- }
- if (mTouchInProgress && action == ACTION_DOWN) {
- Log.w(TAG, "Received down motion while touch was already in progress");
- return false;
- }
-
- if (action == ACTION_DOWN) {
- mTouchInProgress = true;
- if (mInputConsumer == null) {
- mInputConsumer = mInputProxySupplier.get();
- }
- } else if (action == ACTION_CANCEL || action == ACTION_UP) {
- // Finish any pending actions
- mTouchInProgress = false;
- if (mDisableInputProxyPending) {
- mDisableInputProxyPending = false;
- disableInputProxy();
- }
- }
- if (mInputConsumer != null) {
- mInputConsumer.onMotionEvent(ev);
- }
-
- return true;
- }
}
diff --git a/quickstep/src/com/android/quickstep/SysUINavigationMode.java b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
index 6994d66..6b50218 100644
--- a/quickstep/src/com/android/quickstep/SysUINavigationMode.java
+++ b/quickstep/src/com/android/quickstep/SysUINavigationMode.java
@@ -17,6 +17,9 @@
package com.android.quickstep;
import static com.android.launcher3.ResourceUtils.INVALID_RESOURCE_HANDLE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_2_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_3_BUTTON;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON;
import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
import android.content.BroadcastReceiver;
@@ -25,6 +28,7 @@
import android.util.Log;
import com.android.launcher3.ResourceUtils;
+import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.MainThreadInitializedObject;
@@ -38,16 +42,18 @@
public class SysUINavigationMode {
public enum Mode {
- THREE_BUTTONS(false, 0),
- TWO_BUTTONS(true, 1),
- NO_BUTTON(true, 2);
+ THREE_BUTTONS(false, 0, LAUNCHER_NAVIGATION_MODE_3_BUTTON),
+ TWO_BUTTONS(true, 1, LAUNCHER_NAVIGATION_MODE_2_BUTTON),
+ NO_BUTTON(true, 2, LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON);
public final boolean hasGestures;
public final int resValue;
+ public final LauncherEvent launcherEvent;
- Mode(boolean hasGestures, int resValue) {
+ Mode(boolean hasGestures, int resValue, LauncherEvent launcherEvent) {
this.hasGestures = hasGestures;
this.resValue = resValue;
+ this.launcherEvent = launcherEvent;
}
}
@@ -183,12 +189,10 @@
}
public interface NavigationModeChangeListener {
-
void onNavigationModeChanged(Mode newMode);
}
public interface OneHandedModeChangeListener {
-
void onOneHandedModeChanged(int newGesturalHeight);
}
}
\ No newline at end of file
diff --git a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
index 0e2312b..81c4d0c 100644
--- a/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
+++ b/quickstep/src/com/android/quickstep/interaction/NavBarGestureHandler.java
@@ -16,6 +16,7 @@
package com.android.quickstep.interaction;
import static com.android.launcher3.Utilities.squaredHypot;
+import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_COMPLETED;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_BAD_ANGLE;
import static com.android.quickstep.interaction.NavBarGestureHandler.NavBarGestureResult.ASSISTANT_NOT_STARTED_SWIPE_TOO_SHORT;
@@ -48,6 +49,7 @@
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.util.VibratorWrapper;
import com.android.quickstep.SysUINavigationMode.Mode;
+import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
import com.android.quickstep.util.TriggerSwipeUpTouchTracker;
import com.android.systemui.shared.system.QuickStepContract;
@@ -74,6 +76,7 @@
private final PointF mAssistantStartDragPos = new PointF();
private final PointF mDownPos = new PointF();
private final PointF mLastPos = new PointF();
+ private final MotionPauseDetector mMotionPauseDetector;
private boolean mTouchCameFromAssistantCorner;
private boolean mTouchCameFromNavBar;
private boolean mPassedAssistantSlop;
@@ -100,6 +103,7 @@
new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
new NavBarPosition(Mode.NO_BUTTON, displayRotation),
null /*onInterceptTouch*/, this);
+ mMotionPauseDetector = new MotionPauseDetector(context);
final Resources resources = context.getResources();
mBottomGestureHeight =
@@ -177,12 +181,14 @@
}
mLaunchedAssistant = false;
mSwipeUpTouchTracker.init();
+ mMotionPauseDetector.clear();
+ mMotionPauseDetector.setOnMotionPauseListener(this::onMotionPauseChanged);
break;
case MotionEvent.ACTION_MOVE:
+ mLastPos.set(event.getX(), event.getY());
if (!mAssistantGestureActive) {
break;
}
- mLastPos.set(event.getX(), event.getY());
if (!mPassedAssistantSlop) {
// Normal gesture, ensure we pass the slop before we start tracking the gesture
@@ -213,6 +219,8 @@
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
+ mMotionPauseDetector.clear();
+ mMotionPauseDetector.setOnMotionPauseListener(null);
if (mGestureCallback != null && !intercepted && mTouchCameFromNavBar) {
mGestureCallback.onNavBarGestureAttempted(
HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION, new PointF());
@@ -239,9 +247,17 @@
}
mSwipeUpTouchTracker.onMotionEvent(event);
mAssistantGestureDetector.onTouchEvent(event);
+ mMotionPauseDetector.addPosition(event);
+ mMotionPauseDetector.setDisallowPause(mLastPos.y >= mDisplaySize.y - mBottomGestureHeight);
return intercepted;
}
+ protected void onMotionPauseChanged(boolean isPaused) {
+ if (isPaused) {
+ VibratorWrapper.INSTANCE.get(mContext).vibrate(OVERVIEW_HAPTIC);
+ }
+ }
+
/**
* Determine if angle is larger than threshold for assistant detection
*/
diff --git a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
index 4110b33..044e010 100644
--- a/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
+++ b/quickstep/src/com/android/quickstep/interaction/SwipeUpGestureTutorialController.java
@@ -18,7 +18,7 @@
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.util.DefaultDisplay.getSingleFrameMs;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
-import static com.android.quickstep.BaseSwipeUpHandlerV2.MAX_SWIPE_DURATION;
+import static com.android.quickstep.AbsSwipeUpHandler.MAX_SWIPE_DURATION;
import static com.android.quickstep.interaction.TutorialController.TutorialType.HOME_NAVIGATION_COMPLETE;
import static com.android.quickstep.interaction.TutorialController.TutorialType.OVERVIEW_NAVIGATION_COMPLETE;
diff --git a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
index b978c09..059d158 100644
--- a/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
+++ b/quickstep/src/com/android/quickstep/logging/StatsLogCompatManager.java
@@ -59,6 +59,7 @@
import com.android.systemui.shared.system.SysUiStatsLog;
import java.util.ArrayList;
+import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -100,21 +101,22 @@
}
/**
- * Logs the workspace layout information on the model thread.
+ * Logs impression of the current workspace with additional launcher events.
*/
@Override
- public void logSnapshot() {
+ public void logSnapshot(List<EventEnum> extraEvents) {
LauncherAppState.getInstance(mContext).getModel().enqueueModelUpdateTask(
- new SnapshotWorker());
+ new SnapshotWorker(extraEvents));
}
private class SnapshotWorker extends BaseModelUpdateTask {
-
private final InstanceId mInstanceId;
+ private final List<EventEnum> mExtraEvents;
- SnapshotWorker() {
- mInstanceId = new InstanceIdSequence(
- 1 << 20 /*InstanceId.INSTANCE_ID_MAX*/).newInstanceId();
+ SnapshotWorker(List<EventEnum> extraEvents) {
+ mInstanceId = new InstanceIdSequence(1 << 20 /*InstanceId.INSTANCE_ID_MAX*/)
+ .newInstanceId();
+ this.mExtraEvents = extraEvents;
}
@Override
@@ -155,6 +157,9 @@
LauncherAtom.ItemInfo atomInfo = info.buildProto(null);
writeSnapshot(atomInfo, mInstanceId);
}
+ mExtraEvents
+ .forEach(eventName -> logger().withInstanceId(mInstanceId).log(eventName));
+
getDevicePrefs(mContext).edit()
.putLong(LAST_SNAPSHOT_TIME_MILLIS, currentTimeMillis()).apply();
}
diff --git a/res/layout/home_settings.xml b/res/layout/home_settings.xml
new file mode 100644
index 0000000..0f2461a
--- /dev/null
+++ b/res/layout/home_settings.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <EditText
+ android:id="@+id/filter_box"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginHorizontal="@dimen/developer_options_filter_margins"
+ android:hint="@string/developer_options_filter_hint"
+ android:visibility="gone"
+ />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@android:id/list_container"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 947e635..969765f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -247,6 +247,9 @@
<dimen name="snackbar_min_text_size">12sp</dimen>
<dimen name="snackbar_max_text_size">14sp</dimen>
+<!-- Developer Options -->
+ <dimen name="developer_options_filter_margins">10dp</dimen>
+
<!-- Theming related -->
<dimen name="default_dialog_corner_radius">8dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 935bb40..80b511a 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -348,7 +348,8 @@
<!-- content description for paused work apps list -->
<string name="work_apps_paused_content_description">Work profile is paused. Work apps can\’t send you notifications, use your battery, or access your location</string>
-
+ <!-- A hint shown in launcher settings develop options filter box -->
+ <string name="developer_options_filter_hint">Filter</string>
<!-- A tip shown pointing at work toggle -->
<string name="work_switch_tip">Pause work apps and notifications</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 25f21f3..3b9532e 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -149,6 +149,15 @@
<style name="HomeSettingsTheme" parent="@android:style/Theme.DeviceDefault.Settings">
<item name="android:navigationBarColor">@android:color/transparent</item>
+ <item name="preferenceTheme">@style/HomeSettingsPreferenceTheme</item>
+ </style>
+
+ <style name="HomeSettingsPreferenceTheme" parent="@style/PreferenceThemeOverlay.v14.Material">
+ <item name="preferenceFragmentCompatStyle">@style/HomeSettingsFragmentCompatStyle</item>
+ </style>
+
+ <style name="HomeSettingsFragmentCompatStyle" parent="@style/PreferenceFragment.Material">
+ <item name="android:layout">@layout/home_settings</item>
</style>
<!--
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 8c0a2d7..e49c455 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1550,7 +1550,6 @@
mOverlayManager.onActivityDestroyed(this);
mAppTransitionManager.unregisterRemoteAnimations();
mUserChangedCallbackCloseable.close();
- mAllAppsController.onActivityDestroyed();
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -2495,7 +2494,7 @@
* @param updated list of shortcuts which have changed.
*/
@Override
- public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) {
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) {
if (!updated.isEmpty()) {
mWorkspace.updateShortcuts(updated);
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index a6283ff..15e0daa 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -115,6 +115,7 @@
import java.util.ArrayList;
import java.util.HashSet;
+import java.util.List;
import java.util.function.Predicate;
/**
@@ -3087,7 +3088,7 @@
return false;
}
- void updateShortcuts(ArrayList<WorkspaceItemInfo> shortcuts) {
+ void updateShortcuts(List<WorkspaceItemInfo> shortcuts) {
final HashSet<WorkspaceItemInfo> updates = new HashSet<>(shortcuts);
ItemOperator op = (info, v) -> {
if (v instanceof BubbleTextView && updates.contains(info)) {
diff --git a/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
new file mode 100644
index 0000000..5af9113
--- /dev/null
+++ b/src/com/android/launcher3/allapps/AllAppsInsetTransitionController.java
@@ -0,0 +1,186 @@
+/*
+ * 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.launcher3.allapps;
+
+import android.graphics.Insets;
+import android.os.Build;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimationControlListener;
+import android.view.WindowInsetsAnimationController;
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.RequiresApi;
+import androidx.core.os.BuildCompat;
+
+/**
+ * Handles IME over all apps to be synchronously transitioning along with the passed in
+ * root inset.
+ */
+public class AllAppsInsetTransitionController {
+
+ private static final boolean DEBUG = false;
+ private static final String TAG = "AllAppsInsetTransitionController";
+ private static final Interpolator LINEAR = new LinearInterpolator();
+
+ private WindowInsetsAnimationController mAnimationController;
+ private WindowInsetsAnimationControlListener mCurrentRequest;
+
+ private float mAllAppsHeight;
+
+ private int mDownInsetBottom;
+ private boolean mShownAtDown;
+
+ private int mHiddenInsetBottom;
+ private int mShownInsetBottom;
+
+ private float mDown, mCurrent;
+ private View mApps;
+
+ public AllAppsInsetTransitionController(float allAppsHeight, View appsView) {
+ mAllAppsHeight = allAppsHeight;
+ mApps = appsView;
+ }
+
+ /**
+ * Initializes member variables and requests for the {@link WindowInsetsAnimationController}
+ * object.
+ *
+ * @param progress value between 0..1
+ */
+ @RequiresApi(api = Build.VERSION_CODES.R)
+ public void onDragStart(float progress) {
+ if (!BuildCompat.isAtLeastR()) return;
+ onAnimationEnd(progress);
+
+ mDown = progress * mAllAppsHeight;
+
+ // Below two values are sometimes incorrect. Possibly a platform bug
+ mDownInsetBottom = mApps.getRootWindowInsets().getInsets(WindowInsets.Type.ime()).bottom;
+ mShownAtDown = mApps.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+
+ // override this value based on what it should actually be.
+ mShownAtDown = Float.compare(progress, 1f) == 0 ? false : true;
+ mDownInsetBottom = mShownAtDown ? mShownInsetBottom : mHiddenInsetBottom;
+ if (DEBUG) {
+ Log.d(TAG, "\nonDragStart mDownInsets=" + mDownInsetBottom
+ + " mShownAtDown =" + mShownAtDown);
+ }
+
+ mApps.getWindowInsetsController().controlWindowInsetsAnimation(
+ WindowInsets.Type.ime(), -1 /* no predetermined duration */, LINEAR, null,
+ mCurrentRequest = new WindowInsetsAnimationControlListener() {
+
+ @Override
+ public void onReady(WindowInsetsAnimationController controller, int types) {
+ if (DEBUG) {
+ Log.d(TAG, "Listener.onReady " + (mCurrentRequest == this));
+ }
+ if (mCurrentRequest == this) {
+ mAnimationController = controller;
+ } else {
+ controller.finish(mShownAtDown);
+ }
+ }
+
+ @Override
+ public void onFinished(WindowInsetsAnimationController controller) {
+ // when screen lock happens, then this method get called
+ mAnimationController.finish(false);
+ mAnimationController = null;
+ if (DEBUG) {
+ Log.d(TAG, "Listener.onFinished ctrl=" + controller);
+ }
+ }
+
+ @Override
+ public void onCancelled(@Nullable WindowInsetsAnimationController controller) {
+ mAnimationController = null;
+ if (controller != null) {
+ controller.finish(mShownAtDown);
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Listener.onCancelled ctrl=" + controller);
+ }
+ }
+ });
+ }
+
+ /**
+ * Handles the translation using the progress.
+ *
+ * @param progress value between 0..1
+ */
+ @RequiresApi(api = 30)
+ public void setProgress(float progress) {
+ if (!BuildCompat.isAtLeastR()) return;
+ // progress that equals to 0 or 1 is error prone. Do not use them.
+ // Instead use onDragStart and onAnimationEnd
+ if (mAnimationController == null || progress <= 0f || progress >= 1f) return;
+
+ mCurrent = progress * mAllAppsHeight;
+ mHiddenInsetBottom = mAnimationController.getHiddenStateInsets().bottom; // 0
+ mShownInsetBottom = mAnimationController.getShownStateInsets().bottom; // 1155
+
+ int shift = mShownAtDown ? 0 : (int) (mAllAppsHeight - mShownInsetBottom);
+
+ int inset = (int) (mDownInsetBottom + (mDown - mCurrent) - shift);
+
+ if (DEBUG) {
+ Log.d(TAG, "updateInset mCurrent=" + mCurrent + " mDown="
+ + mDown + " hidden=" + mHiddenInsetBottom
+ + " shown=" + mShownInsetBottom
+ + " mDownInsets.bottom=" + mDownInsetBottom + " inset:" + inset
+ + " shift: " + shift);
+ }
+ final int start = mShownAtDown ? mShownInsetBottom : mHiddenInsetBottom;
+ final int end = mShownAtDown ? mHiddenInsetBottom : mShownInsetBottom;
+ inset = Math.max(inset, mHiddenInsetBottom);
+ inset = Math.min(inset, mShownInsetBottom);
+ Log.d(TAG, "updateInset inset:" + inset);
+
+ mAnimationController.setInsetsAndAlpha(
+ Insets.of(0, 0, 0, inset),
+ 1f, (inset - start) / (float) (end - start));
+ }
+
+ /**
+ * Report to the animation controller that we no longer plan to translate anymore.
+ *
+ * @param progress value between 0..1
+ */
+ @RequiresApi(api = 30)
+ public void onAnimationEnd(float progress) {
+ if (DEBUG) {
+ Log.d(TAG, "endTranslation progress=" + progress
+ + " mAnimationController=" + mAnimationController);
+ }
+
+ if (mAnimationController == null) return;
+
+ if (Float.compare(progress, 1f) == 0 /* bottom */) {
+ mAnimationController.finish(false /* gone */);
+ }
+ if (Float.compare(progress, 0f) == 0 /* top */) {
+ mAnimationController.finish(true /* show */);
+ }
+ mAnimationController = null;
+ mCurrentRequest = null;
+ }
+}
diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
index a9b030e..5b00631 100644
--- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java
+++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2015 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.allapps;
import static com.android.launcher3.LauncherState.ALL_APPS_CONTENT;
@@ -14,31 +29,28 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
+import static com.android.launcher3.util.SystemUiController.UI_STATE_ALLAPPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
-import android.content.Context;
import android.util.FloatProperty;
import android.view.View;
-import android.view.ViewGroup;
import android.view.animation.Interpolator;
-import android.widget.EditText;
+
+import androidx.core.os.BuildCompat;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.views.ScrimView;
-import com.android.systemui.plugins.AllAppsSearchPlugin;
-import com.android.systemui.plugins.PluginListener;
/**
* Handles AllApps view transition.
@@ -51,7 +63,7 @@
* closer to top or closer to the page indicator.
*/
public class AllAppsTransitionController implements StateHandler<LauncherState>,
- OnDeviceProfileChangeListener, PluginListener<AllAppsSearchPlugin> {
+ OnDeviceProfileChangeListener {
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
@@ -85,10 +97,7 @@
private float mProgress; // [0, 1], mShiftRange * mProgress = shiftCurrent
private float mScrollRangeDelta = 0;
-
- // plugin related variables
- private AllAppsSearchPlugin mPlugin;
- private View mPluginContent;
+ private AllAppsInsetTransitionController mInsetController;
public AllAppsTransitionController(Launcher l) {
mLauncher = l;
@@ -103,6 +112,10 @@
return mShiftRange;
}
+ public AllAppsInsetTransitionController getInsetController() {
+ return mInsetController;
+ }
+
@Override
public void onDeviceProfileChanged(DeviceProfile dp) {
mIsVerticalLayout = dp.isVerticalBarLayout();
@@ -130,8 +143,8 @@
float shiftCurrent = progress * mShiftRange;
mAppsView.setTranslationY(shiftCurrent);
- if (mPlugin != null) {
- mPlugin.setProgress(progress);
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
+ mInsetController.setProgress(progress);
}
}
@@ -201,16 +214,12 @@
Interpolator allAppsFade = config.getInterpolator(ANIM_ALL_APPS_FADE, LINEAR);
Interpolator headerFade = config.getInterpolator(ANIM_ALL_APPS_HEADER_FADE, allAppsFade);
- if (mPlugin == null) {
- setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
- mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra,
- hasAllAppsContent, setter, headerFade, allAppsFade);
- } else {
- setter.setViewAlpha(mPluginContent, hasAllAppsContent ? 1 : 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getContentView(), 0, allAppsFade);
- setter.setViewAlpha(mAppsView.getScrollBar(), 0, allAppsFade);
- }
+
+ setter.setViewAlpha(mAppsView.getContentView(), hasAllAppsContent ? 1 : 0, allAppsFade);
+ setter.setViewAlpha(mAppsView.getScrollBar(), hasAllAppsContent ? 1 : 0, allAppsFade);
+ mAppsView.getFloatingHeaderView().setContentVisibility(hasHeaderExtra,
+ hasAllAppsContent, setter, headerFade, allAppsFade);
+
mAppsView.getSearchUiManager().setContentVisibility(visibleElements, setter, allAppsFade);
setter.setInt(mScrimView, ScrimView.DRAG_HANDLE_ALPHA,
@@ -228,8 +237,12 @@
public void setupViews(AllAppsContainerView appsView, ScrimView scrimView) {
mAppsView = appsView;
mScrimView = scrimView;
- PluginManagerWrapper.INSTANCE.get(mLauncher)
- .addPluginListener(this, AllAppsSearchPlugin.class, false);
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()) {
+ mInsetController = new AllAppsInsetTransitionController(mShiftRange, mAppsView);
+ mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+ }
}
/**
@@ -252,47 +265,11 @@
if (Float.compare(mProgress, 1f) == 0) {
mAppsView.reset(false /* animate */);
}
- updatePluginAnimationEnd();
- }
-
- @Override
- public void onPluginConnected(AllAppsSearchPlugin plugin, Context context) {
- mPlugin = plugin;
- mPluginContent = mLauncher.getLayoutInflater().inflate(
- R.layout.all_apps_content_layout, mAppsView, false);
- mAppsView.addView(mPluginContent);
- mPluginContent.setAlpha(0f);
- mPlugin.setup((ViewGroup) mPluginContent, mLauncher, mShiftRange);
- }
-
- @Override
- public void onPluginDisconnected(AllAppsSearchPlugin plugin) {
- mPlugin = null;
- mAppsView.removeView(mPluginContent);
- }
-
- public void onActivityDestroyed() {
- PluginManagerWrapper.INSTANCE.get(mLauncher).removePluginListener(this);
- }
-
- /** Used for the plugin to signal when drag starts happens
- * @param toAllApps*/
- public void onDragStart(boolean toAllApps) {
- if (mPlugin == null) return;
-
- if (toAllApps) {
- EditText editText = mAppsView.getSearchUiManager().setTextSearchEnabled(true);
- mPlugin.setEditText(editText);
- }
- mPlugin.onDragStart(toAllApps ? 1f : 0f);
- }
-
- private void updatePluginAnimationEnd() {
- if (mPlugin == null) return;
- mPlugin.onAnimationEnd(mProgress);
- if (Float.compare(mProgress, 1f) == 0) {
- mAppsView.getSearchUiManager().setTextSearchEnabled(false);
- mPlugin.setEditText(null);
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()) {
+ mInsetController.onAnimationEnd(mProgress);
+ if (Float.compare(mProgress, 1f) == 0) {
+ mAppsView.getSearchUiManager().setTextSearchEnabled(true).requestFocus();
+ }
}
}
}
diff --git a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
index 1d32d1d..30c3417 100644
--- a/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
+++ b/src/com/android/launcher3/compat/AccessibilityManagerCompat.java
@@ -70,7 +70,8 @@
final Bundle parcel = new Bundle();
parcel.putInt(TestProtocol.STATE_FIELD, stateOrdinal);
- sendEventToTest(accessibilityManager, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
+ sendEventToTest(
+ accessibilityManager, context, TestProtocol.SWITCHED_TO_STATE_MESSAGE, parcel);
Log.d(TestProtocol.PERMANENT_DIAG_TAG, "sendStateEventToTest: " + stateOrdinal);
}
@@ -78,22 +79,24 @@
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
- sendEventToTest(accessibilityManager, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
+ sendEventToTest(accessibilityManager, context, TestProtocol.SCROLL_FINISHED_MESSAGE, null);
}
public static void sendPauseDetectedEventToTest(Context context) {
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
- sendEventToTest(accessibilityManager, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
+ sendEventToTest(accessibilityManager, context, TestProtocol.PAUSE_DETECTED_MESSAGE, null);
}
private static void sendEventToTest(
- AccessibilityManager accessibilityManager, String eventTag, Bundle data) {
+ AccessibilityManager accessibilityManager,
+ Context context, String eventTag, Bundle data) {
final AccessibilityEvent e = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_ANNOUNCEMENT);
e.setClassName(eventTag);
e.setParcelableData(data);
+ e.setPackageName(context.getApplicationContext().getPackageName());
accessibilityManager.sendAccessibilityEvent(e);
}
diff --git a/src/com/android/launcher3/config/FeatureFlags.java b/src/com/android/launcher3/config/FeatureFlags.java
index 78e8b82..0ab74af 100644
--- a/src/com/android/launcher3/config/FeatureFlags.java
+++ b/src/com/android/launcher3/config/FeatureFlags.java
@@ -181,6 +181,10 @@
public static final BooleanFlag USER_EVENT_DISPATCHER = new DeviceFlag(
"USER_EVENT_DISPATCHER", true, "User event dispatcher collects logs.");
+ public static final BooleanFlag ENABLE_MINIMAL_DEVICE = new DeviceFlag(
+ "ENABLE_MINIMAL_DEVICE", false,
+ "Allow user to toggle minimal device mode in launcher.");
+
public static void initialize(Context context) {
synchronized (sDebugFlags) {
for (DebugFlag flag : sDebugFlags) {
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 75275b2..32d061c 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -129,6 +129,8 @@
private float mDotScale;
private Animator mDotScaleAnim;
+ private Rect mTouchArea = new Rect();
+
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
private float mScaleForReorderBounce = 1f;
@@ -711,6 +713,11 @@
@Override
public boolean onTouchEvent(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN
+ && shouldIgnoreTouchDown(event.getX(), event.getY())) {
+ return false;
+ }
+
// Call the superclass onTouchEvent first, because sometimes it changes the state to
// isPressed() on an ACTION_UP
super.onTouchEvent(event);
@@ -719,6 +726,15 @@
return true;
}
+ /**
+ * Returns true if the touch down at the provided position be ignored
+ */
+ protected boolean shouldIgnoreTouchDown(float x, float y) {
+ mTouchArea.set(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(),
+ getHeight() - getPaddingBottom());
+ return !mTouchArea.contains((int) x, (int) y);
+ }
+
@Override
public void cancelLongPress() {
super.cancelLongPress();
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index acf4482..de72534 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -30,11 +30,16 @@
import com.android.launcher3.userevent.LauncherLogProto;
import com.android.launcher3.util.ResourceBasedOverride;
+import java.util.List;
+
/**
* Handles the user event logging in R+.
+ *
+ * <pre>
* All of the event ids are defined here.
* Most of the methods are dummy methods for Launcher3
* Actual call happens only for Launcher variant that implements QuickStep.
+ * </pre>
*/
public class StatsLogManager implements ResourceBasedOverride {
@@ -46,8 +51,8 @@
public static final int LAUNCHER_STATE_UNCHANGED = 5;
/**
- * Returns proper launcher state enum for {@link StatsLogManager}
- * (to be removed during UserEventDispatcher cleanup)
+ * Returns proper launcher state enum for {@link StatsLogManager}(to be removed during
+ * UserEventDispatcher cleanup)
*/
public static int containerTypeToAtomState(int containerType) {
switch (containerType) {
@@ -64,9 +69,8 @@
}
/**
- * Returns event enum based on the two {@link ContainerType} transition information when
- * swipe gesture happens.
- * (to be removed during UserEventDispatcher cleanup)
+ * Returns event enum based on the two {@link ContainerType} transition information when swipe
+ * gesture happens(to be removed during UserEventDispatcher cleanup).
*/
public static EventEnum getLauncherAtomEvent(int startContainerType,
int targetContainerType, EventEnum fallbackEvent) {
@@ -270,7 +274,46 @@
LAUNCHER_SELECT_MODE_CLOSE(583),
@UiEvent(doc = "User tapped on the highlight items in select mode")
- LAUNCHER_SELECT_MODE_ITEM(584);
+ LAUNCHER_SELECT_MODE_ITEM(584),
+
+ @UiEvent(doc = "Notification dot on app icon enabled.")
+ LAUNCHER_NOTIFICATION_DOT_ENABLED(611),
+
+ @UiEvent(doc = "Notification dot on app icon disabled.")
+ LAUNCHER_NOTIFICATION_DOT_DISABLED(612),
+
+ @UiEvent(doc = "For new apps, add app icons to home screen enabled.")
+ LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_ENABLED(613),
+
+ @UiEvent(doc = "For new apps, add app icons to home screen disabled.")
+ LAUNCHER_ADD_NEW_APPS_TO_HOME_SCREEN_DISABLED(614),
+
+ @UiEvent(doc = "Home screen rotation is enabled when phone is rotated.")
+ LAUNCHER_HOME_SCREEN_ROTATION_ENABLED(615),
+
+ @UiEvent(doc = "Home screen rotation is disabled when phone is rotated.")
+ LAUNCHER_HOME_SCREEN_ROTATION_DISABLED(616),
+
+ @UiEvent(doc = "Suggestions in all apps list enabled.")
+ LAUNCHER_ALL_APPS_SUGGESTIONS_ENABLED(619),
+
+ @UiEvent(doc = "Suggestions in all apps list disabled.")
+ LAUNCHER_ALL_APPS_SUGGESTIONS_DISABLED(620),
+
+ @UiEvent(doc = "Suggestions on home screen is enabled.")
+ LAUNCHER_HOME_SCREEN_SUGGESTIONS_ENABLED(621),
+
+ @UiEvent(doc = "Suggestions on home screen is disabled.")
+ LAUNCHER_HOME_SCREEN_SUGGESTIONS_DISABLED(622),
+
+ @UiEvent(doc = "System navigation is 3 button mode.")
+ LAUNCHER_NAVIGATION_MODE_3_BUTTON(623),
+
+ @UiEvent(doc = "System navigation mode is 2 button mode.")
+ LAUNCHER_NAVIGATION_MODE_2_BUTTON(624),
+
+ @UiEvent(doc = "System navigation mode is 0 button mode/gesture navigation mode .")
+ LAUNCHER_NAVIGATION_MODE_GESTURE_BUTTON(625);
// ADD MORE
@@ -401,8 +444,8 @@
}
/**
- * Logs snapshot, or impression of the current workspace.
+ * Logs impression of the current workspace with additional launcher events.
*/
- public void logSnapshot() {
+ public void logSnapshot(List<EventEnum> additionalEvents) {
}
}
diff --git a/src/com/android/launcher3/model/BaseModelUpdateTask.java b/src/com/android/launcher3/model/BaseModelUpdateTask.java
index 9013cba..d1e5017 100644
--- a/src/com/android/launcher3/model/BaseModelUpdateTask.java
+++ b/src/com/android/launcher3/model/BaseModelUpdateTask.java
@@ -22,7 +22,9 @@
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.model.BgDataModel.Callbacks;
+import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
+import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ItemInfoMatcher;
@@ -30,7 +32,10 @@
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
+import java.util.stream.Collectors;
/**
* Extension of {@link ModelUpdateTask} with some utility methods
@@ -88,11 +93,27 @@
return mModel.getWriter(false /* hasVerticalHotseat */, false /* verifyChanges */);
}
-
- public void bindUpdatedWorkspaceItems(final ArrayList<WorkspaceItemInfo> updatedShortcuts) {
- if (!updatedShortcuts.isEmpty()) {
- scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(updatedShortcuts));
+ public void bindUpdatedWorkspaceItems(List<WorkspaceItemInfo> allUpdates) {
+ // Bind workspace items
+ List<WorkspaceItemInfo> workspaceUpdates = allUpdates.stream()
+ .filter(info -> info.id != ItemInfo.NO_ID)
+ .collect(Collectors.toList());
+ if (!workspaceUpdates.isEmpty()) {
+ scheduleCallbackTask(c -> c.bindWorkspaceItemsChanged(workspaceUpdates));
}
+
+ // Bind extra items if any
+ allUpdates.stream()
+ .mapToInt(info -> info.container)
+ .distinct()
+ .mapToObj(mDataModel.extraItems::get)
+ .filter(Objects::nonNull)
+ .forEach(this::bindExtraContainerItems);
+ }
+
+ public void bindExtraContainerItems(FixedContainerItems item) {
+ FixedContainerItems copy = item.clone();
+ scheduleCallbackTask(c -> c.bindExtraContainerItems(copy));
}
public void bindDeepShortcuts(BgDataModel dataModel) {
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index 7524920..dfdc138 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -15,19 +15,25 @@
*/
package com.android.launcher3.model;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY;
+
import static com.android.launcher3.model.WidgetsModel.GO_DISABLE_WIDGETS;
import static com.android.launcher3.shortcuts.ShortcutRequest.PINNED;
+import static java.util.stream.Collectors.groupingBy;
+import static java.util.stream.Collectors.mapping;
+
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.ShortcutInfo;
import android.os.UserHandle;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
-import android.util.MutableInt;
import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.LauncherSettings;
+import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo;
@@ -36,8 +42,10 @@
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
+import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.launcher3.shortcuts.ShortcutRequest.QueryResult;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
@@ -50,14 +58,16 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.function.BiConsumer;
+import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* All the data stored in-memory and managed by the LauncherModel
@@ -89,9 +99,9 @@
public final IntSparseArrayMap<FolderInfo> folders = new IntSparseArrayMap<>();
/**
- * Map of ShortcutKey to the number of times it is pinned.
+ * Extra container based items
*/
- public final Map<ShortcutKey, MutableInt> pinnedShortcutCounts = new HashMap<>();
+ public final IntSparseArrayMap<FixedContainerItems> extraItems = new IntSparseArrayMap<>();
/**
* List of all cached predicted items visible on home screen
@@ -128,8 +138,8 @@
appWidgets.clear();
folders.clear();
itemsIdMap.clear();
- pinnedShortcutCounts.clear();
deepShortcutMap.clear();
+ extraItems.clear();
}
/**
@@ -182,6 +192,7 @@
}
public synchronized void removeItem(Context context, Iterable<? extends ItemInfo> items) {
+ ArraySet<UserHandle> updatedDeepShortcuts = new ArraySet<>();
for (ItemInfo item : items) {
switch (item.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_FOLDER:
@@ -200,14 +211,7 @@
workspaceItems.remove(item);
break;
case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
- // Decrement pinned shortcut count
- ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
- MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
- if ((count == null || --count.value == 0)
- && !InstallShortcutReceiver.getPendingShortcuts(context)
- .contains(pinnedShortcut)) {
- unpinShortcut(context, pinnedShortcut);
- }
+ updatedDeepShortcuts.add(item.user);
// Fall through.
}
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
@@ -221,6 +225,7 @@
}
itemsIdMap.remove(item.id);
}
+ updatedDeepShortcuts.forEach(user -> updateShortcutPinnedState(context, user));
}
public synchronized void addItem(Context context, ItemInfo item, boolean newItem) {
@@ -230,23 +235,7 @@
folders.put(item.id, (FolderInfo) item);
workspaceItems.add(item);
break;
- case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT: {
- // Increment the count for the given shortcut
- ShortcutKey pinnedShortcut = ShortcutKey.fromItemInfo(item);
- MutableInt count = pinnedShortcutCounts.get(pinnedShortcut);
- if (count == null) {
- count = new MutableInt(1);
- pinnedShortcutCounts.put(pinnedShortcut, count);
- } else {
- count.value++;
- }
-
- // Since this is a new item, pin the shortcut in the system server.
- if (newItem && count.value == 1) {
- updatePinnedShortcuts(context, pinnedShortcut, List::add);
- }
- // Fall through
- }
+ case LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP ||
@@ -271,36 +260,87 @@
appWidgets.add((LauncherAppWidgetInfo) item);
break;
}
+ if (newItem && item.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
+ updateShortcutPinnedState(context, item.user);
+ }
}
/**
- * Removes the given shortcut from the current list of pinned shortcuts.
- * (Runs on background thread)
+ * Updates the deep shortucts state in system to match out internal model, pinning any missing
+ * shortcuts and unpinning any extra shortcuts.
*/
- public void unpinShortcut(Context context, ShortcutKey key) {
- updatePinnedShortcuts(context, key, List::remove);
+ public void updateShortcutPinnedState(Context context) {
+ for (UserHandle user : UserCache.INSTANCE.get(context).getUserProfiles()) {
+ updateShortcutPinnedState(context, user);
+ }
}
- private void updatePinnedShortcuts(Context context, ShortcutKey key,
- BiConsumer<List<String>, String> idOp) {
+ /**
+ * Updates the deep shortucts state in system to match out internal model, pinning any missing
+ * shortcuts and unpinning any extra shortcuts.
+ */
+ public synchronized void updateShortcutPinnedState(Context context, UserHandle user) {
if (GO_DISABLE_WIDGETS) {
return;
}
- String packageName = key.componentName.getPackageName();
- String id = key.getId();
- UserHandle user = key.user;
- List<String> pinnedIds = new ShortcutRequest(context, user)
- .forPackage(packageName)
- .query(PINNED)
- .stream()
- .map(ShortcutInfo::getId)
- .collect(Collectors.toCollection(ArrayList::new));
- idOp.accept(pinnedIds, id);
- try {
- context.getSystemService(LauncherApps.class).pinShortcuts(packageName, pinnedIds, user);
- } catch (SecurityException | IllegalStateException e) {
- Log.w(TAG, "Failed to pin shortcut", e);
+
+ // Collect all system shortcuts
+ QueryResult result = new ShortcutRequest(context, user)
+ .query(PINNED | FLAG_GET_KEY_FIELDS_ONLY);
+ if (!result.wasSuccess()) {
+ return;
}
+ // Map of packageName to shortcutIds that are currently in the system
+ Map<String, Set<String>> systemMap = result.stream()
+ .collect(groupingBy(ShortcutInfo::getPackage,
+ mapping(ShortcutInfo::getId, Collectors.toSet())));
+
+ // Collect all model shortcuts
+ Stream.Builder<WorkspaceItemInfo> itemStream = Stream.builder();
+ forAllWorkspaceItemInfos(user, itemStream::accept);
+ // Map of packageName to shortcutIds that are currently in our model
+ Map<String, Set<String>> modelMap = Stream.concat(
+ // Model shortcuts
+ itemStream.build()
+ .filter(wi -> wi.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
+ .map(ShortcutKey::fromItemInfo),
+ // Pending shortcuts
+ InstallShortcutReceiver.getPendingShortcuts(context)
+ .stream().filter(si -> si.user.equals(user)))
+ .collect(groupingBy(ShortcutKey::getPackageName,
+ mapping(ShortcutKey::getId, Collectors.toSet())));
+
+ // Check for diff
+ for (Map.Entry<String, Set<String>> entry : modelMap.entrySet()) {
+ Set<String> modelShortcuts = entry.getValue();
+ Set<String> systemShortcuts = systemMap.remove(entry.getKey());
+ if (systemShortcuts == null) {
+ systemShortcuts = Collections.emptySet();
+ }
+
+ // Do not use .equals as it can vary based on the type of set
+ if (systemShortcuts.size() != modelShortcuts.size()
+ || !systemShortcuts.containsAll(modelShortcuts)) {
+ // Update system state for this package
+ try {
+ context.getSystemService(LauncherApps.class).pinShortcuts(
+ entry.getKey(), new ArrayList<>(modelShortcuts), user);
+ } catch (SecurityException | IllegalStateException e) {
+ Log.w(TAG, "Failed to pin shortcut", e);
+ }
+ }
+ }
+
+ // If there are any extra pinned shortcuts, remove them
+ systemMap.keySet().forEach(packageName -> {
+ // Update system state
+ try {
+ context.getSystemService(LauncherApps.class).pinShortcuts(
+ packageName, Collections.emptyList(), user);
+ } catch (SecurityException | IllegalStateException e) {
+ Log.w(TAG, "Failed to unpin shortcut", e);
+ }
+ });
}
/**
@@ -360,8 +400,40 @@
op.accept((WorkspaceItemInfo) info);
}
}
+
+ for (int i = extraItems.size() - 1; i >= 0; i--) {
+ for (ItemInfo info : extraItems.valueAt(i).items) {
+ if (info instanceof WorkspaceItemInfo && userHandle.equals(info.user)) {
+ op.accept((WorkspaceItemInfo) info);
+ }
+ }
+ }
}
+ /**
+ * An object containing items corresponding to a fixed container
+ */
+ public static class FixedContainerItems {
+
+ public final int containerId;
+ public final List<ItemInfo> items;
+
+ public FixedContainerItems(int containerId) {
+ this(containerId, new ArrayList<>());
+ }
+
+ public FixedContainerItems(int containerId, List<ItemInfo> items) {
+ this.containerId = containerId;
+ this.items = items;
+ }
+
+ @Override
+ public FixedContainerItems clone() {
+ return new FixedContainerItems(containerId, Collections.unmodifiableList(items));
+ }
+ }
+
+
public interface Callbacks {
// If the launcher has permission to access deep shortcuts.
int FLAG_HAS_SHORTCUT_PERMISSION = 1 << 0;
@@ -384,7 +456,7 @@
void bindAppsAdded(IntArray newScreens,
ArrayList<ItemInfo> addNotAnimated, ArrayList<ItemInfo> addAnimated);
void bindPromiseAppProgressUpdated(PromiseAppInfo app);
- void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated);
+ void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated);
void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets);
void bindRestoreItemsChange(HashSet<ItemInfo> updates);
void bindWorkspaceComponentsRemoved(ItemInfoMatcher matcher);
@@ -393,6 +465,11 @@
void executeOnNextDraw(ViewOnDrawExecutor executor);
void bindDeepShortcutMap(HashMap<ComponentKey, Integer> deepShortcutMap);
+ /**
+ * Binds extra item provided any external source
+ */
+ default void bindExtraContainerItems(FixedContainerItems item) { }
+
void bindAllApplications(AppInfo[] apps, int flags);
/**
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index 4a64522..bea0086 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -46,12 +46,10 @@
import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray;
-import android.util.MutableInt;
import android.util.TimingLogger;
import androidx.annotation.WorkerThread;
-import com.android.launcher3.InstallShortcutReceiver;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
@@ -94,7 +92,6 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CancellationException;
@@ -794,17 +791,8 @@
LauncherSettings.Settings.METHOD_REMOVE_GHOST_WIDGETS);
}
- // Unpin shortcuts that don't exist on the workspace.
- HashSet<ShortcutKey> pendingShortcuts =
- InstallShortcutReceiver.getPendingShortcuts(context);
- for (ShortcutKey key : shortcutKeyToPinnedShortcuts.keySet()) {
- MutableInt numTimesPinned = mBgDataModel.pinnedShortcutCounts.get(key);
- if ((numTimesPinned == null || numTimesPinned.value == 0)
- && !pendingShortcuts.contains(key)) {
- // Shortcut is pinned but doesn't exist on the workspace; unpin it.
- mBgDataModel.unpinShortcut(context, key);
- }
- }
+ // Update pinned state of model shortcuts
+ mBgDataModel.updateShortcutPinnedState(context);
// Sort the folder items, update ranks, and make sure all preview items are high res.
FolderGridOrganizer verifier =
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 5aab41a..2d7d6b0 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -21,8 +21,10 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.LongSparseArray;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.SimpleBroadcastReceiver;
@@ -102,6 +104,9 @@
mUsers = null;
mUserToSerialMap = null;
}
+ if (TestProtocol.sDebugTracing) {
+ Log.d(TestProtocol.WORK_PROFILE_REMOVED, "Work profile removed", new Exception());
+ }
}
}
diff --git a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
index a013312..31c3014 100644
--- a/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
+++ b/src/com/android/launcher3/secondarydisplay/SecondaryDisplayLauncher.java
@@ -20,6 +20,7 @@
import android.app.ActivityOptions;
import android.content.Intent;
import android.os.Bundle;
+import android.view.Display;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewAnimationUtils;
@@ -168,7 +169,9 @@
@Override
public ActivityOptions getActivityLaunchOptions(View v) {
- return null;
+ final Display display = getWindow().getDecorView().getDisplay();
+ return display != null ? ActivityOptions.makeBasic().setLaunchDisplayId(
+ display.getDisplayId()) : null;
}
@Override
@@ -222,7 +225,7 @@
}
@Override
- public void bindWorkspaceItemsChanged(ArrayList<WorkspaceItemInfo> updated) { }
+ public void bindWorkspaceItemsChanged(List<WorkspaceItemInfo> updated) { }
@Override
public void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
diff --git a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
index b12d04f..4baecb7 100644
--- a/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
+++ b/src/com/android/launcher3/settings/DeveloperOptionsFragment.java
@@ -34,17 +34,23 @@
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
+import android.text.Editable;
+import android.text.TextWatcher;
import android.util.ArrayMap;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
+import android.widget.EditText;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceDataStore;
import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;
import androidx.preference.PreferenceViewHolder;
import androidx.preference.SwitchPreference;
@@ -81,6 +87,7 @@
private PreferenceCategory mPluginsCategory;
private FlagTogglerPrefUi mFlagTogglerPrefUi;
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
@@ -99,6 +106,50 @@
maybeAddSandboxCategory();
}
+ private void filterPreferences(String query, PreferenceGroup pg) {
+ int count = pg.getPreferenceCount();
+ int hidden = 0;
+ for (int i = 0; i < count; i++) {
+ Preference preference = pg.getPreference(i);
+ if (preference instanceof PreferenceGroup) {
+ filterPreferences(query, (PreferenceGroup) preference);
+ } else {
+ String title = preference.getTitle().toString().toLowerCase().replace("_", " ");
+ if (query.isEmpty() || title.contains(query)) {
+ preference.setVisible(true);
+ } else {
+ preference.setVisible(false);
+ hidden++;
+ }
+ }
+ }
+ pg.setVisible(hidden != count);
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ EditText filterBox = view.findViewById(R.id.filter_box);
+ filterBox.setVisibility(View.VISIBLE);
+ filterBox.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
+
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ String query = editable.toString().toLowerCase().replace("_", " ");
+ filterPreferences(query, mPreferenceScreen);
+ }
+ });
+ }
+
@Override
public void onDestroy() {
super.onDestroy();
diff --git a/src/com/android/launcher3/shortcuts/ShortcutKey.java b/src/com/android/launcher3/shortcuts/ShortcutKey.java
index 3ca9490..0c6d675 100644
--- a/src/com/android/launcher3/shortcuts/ShortcutKey.java
+++ b/src/com/android/launcher3/shortcuts/ShortcutKey.java
@@ -30,6 +30,10 @@
return componentName.getClassName();
}
+ public String getPackageName() {
+ return componentName.getPackageName();
+ }
+
/**
* Creates a {@link ShortcutRequest} for this key
*/
diff --git a/src/com/android/launcher3/testing/TestProtocol.java b/src/com/android/launcher3/testing/TestProtocol.java
index 8165627..9816f13 100644
--- a/src/com/android/launcher3/testing/TestProtocol.java
+++ b/src/com/android/launcher3/testing/TestProtocol.java
@@ -107,4 +107,5 @@
public static final String PAUSE_NOT_DETECTED = "b/139891609";
public static final String OVERIEW_NOT_ALLAPPS = "b/156095088";
public static final String NO_SWIPE_TO_HOME = "b/158017601";
+ public static final String WORK_PROFILE_REMOVED = "b/159671700";
}
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index 3c78b08..66dbf6a 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -37,6 +37,8 @@
import android.view.HapticFeedbackConstants;
import android.view.MotionEvent;
+import androidx.core.os.BuildCompat;
+
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherState;
@@ -44,6 +46,7 @@
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
+import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.states.StateAnimationConfig;
@@ -265,8 +268,10 @@
mCanBlockFling = mFromState == NORMAL;
mFlingBlockCheck.unblockFling();
// Must be called after all the animation controllers have been paused
- if (mToState == ALL_APPS || mToState == NORMAL) {
- mLauncher.getAllAppsController().onDragStart(mToState == ALL_APPS);
+ if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && BuildCompat.isAtLeastR()
+ && (mToState == ALL_APPS || mToState == NORMAL)) {
+ mLauncher.getAllAppsController().getInsetController().onDragStart(
+ mToState == ALL_APPS ? 0 : 1);
}
}
diff --git a/src/com/android/launcher3/util/SystemUiController.java b/src/com/android/launcher3/util/SystemUiController.java
index 275c024..3e48006 100644
--- a/src/com/android/launcher3/util/SystemUiController.java
+++ b/src/com/android/launcher3/util/SystemUiController.java
@@ -33,6 +33,7 @@
public static final int UI_STATE_SCRIM_VIEW = 1;
public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
public static final int UI_STATE_OVERVIEW = 3;
+ public static final int UI_STATE_ALLAPPS = 4;
public static final int FLAG_LIGHT_NAV = 1 << 0;
public static final int FLAG_DARK_NAV = 1 << 1;
@@ -40,7 +41,7 @@
public static final int FLAG_DARK_STATUS = 1 << 3;
private final Window mWindow;
- private final int[] mStates = new int[4];
+ private final int[] mStates = new int[5];
public SystemUiController(Window window) {
mWindow = window;
diff --git a/tests/src/com/android/launcher3/ui/WorkTabTest.java b/tests/src/com/android/launcher3/ui/WorkTabTest.java
index 8d571ff..f5f93c4 100644
--- a/tests/src/com/android/launcher3/ui/WorkTabTest.java
+++ b/tests/src/com/android/launcher3/ui/WorkTabTest.java
@@ -24,6 +24,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.util.Log;
import android.widget.TextView;
import androidx.test.filters.LargeTest;
@@ -34,6 +35,7 @@
import com.android.launcher3.allapps.AllAppsPagedView;
import com.android.launcher3.allapps.WorkModeSwitch;
import com.android.launcher3.dragndrop.DragLayer;
+import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.WorkEduView;
import org.junit.After;
@@ -132,6 +134,10 @@
l.getResources().getString(R.string.work_profile_edu_personal_apps));
workEduView.findViewById(R.id.proceed).callOnClick();
});
+
+ executeOnLauncher(launcher -> Log.d(TestProtocol.WORK_PROFILE_REMOVED,
+ "Work profile status: " + launcher.getAppsView().isPersonalTabVisible()));
+
// verify work edu is seen next
waitForLauncherCondition("Launcher did not show the next edu screen", l ->
((AllAppsPagedView) l.getAppsView().getContentView()).getCurrentPage() == WORK_PAGE
diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
index a4aa9f2..fa495f5 100644
--- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
+++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java
@@ -192,7 +192,7 @@
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
executeOnLauncher(l -> l.getAppWidgetHost().startListening());
verifyWidgetPresent(info);
- assertNull(mLauncher.getWorkspace().tryGetPendingWidget(DEFAULT_UI_TIMEOUT));
+ assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
}
@Test
diff --git a/tests/tapl/com/android/launcher3/tapl/Background.java b/tests/tapl/com/android/launcher3/tapl/Background.java
index 0c4e5a9..e538f60 100644
--- a/tests/tapl/com/android/launcher3/tapl/Background.java
+++ b/tests/tapl/com/android/launcher3/tapl/Background.java
@@ -93,7 +93,7 @@
mLauncher.sendPointer(
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
- mLauncher.executeAndWaitForEvent(
+ mLauncher.executeAndWaitForLauncherEvent(
() -> mLauncher.movePointer(
downTime,
downTime,
diff --git a/tests/tapl/com/android/launcher3/tapl/Launchable.java b/tests/tapl/com/android/launcher3/tapl/Launchable.java
index 13ecfb8..093c024 100644
--- a/tests/tapl/com/android/launcher3/tapl/Launchable.java
+++ b/tests/tapl/com/android/launcher3/tapl/Launchable.java
@@ -56,15 +56,18 @@
private Background launch(BySelector selector) {
LauncherInstrumentation.log("Launchable.launch before click " +
mObject.getVisibleCenter() + " in " + mLauncher.getVisibleBounds(mObject));
+ final String label = mObject.getText();
mLauncher.executeAndWaitForEvent(
- () -> mLauncher.clickLauncherObject(mObject),
+ () -> {
+ mLauncher.clickLauncherObject(mObject);
+ expectActivityStartEvents();
+ },
event -> event.getEventType() == TYPE_WINDOW_STATE_CHANGED,
- () -> "Launching an app didn't open a new window: " + mObject.getText());
- expectActivityStartEvents();
+ () -> "Launching an app didn't open a new window: " + label);
mLauncher.assertTrue(
- "App didn't start: " + selector,
+ "App didn't start: " + label,
mLauncher.getDevice().wait(Until.hasObject(selector),
LauncherInstrumentation.WAIT_TIME_MS));
return new Background(mLauncher);
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index c74dea9..4e2ed11 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -103,6 +103,7 @@
static final Pattern EVENT_TOUCH_DOWN_TIS = getTouchEventPatternTIS("ACTION_DOWN");
static final Pattern EVENT_TOUCH_UP_TIS = getTouchEventPatternTIS("ACTION_UP");
+ private final String mLauncherPackage;
// Types for launcher containers that the user is interacting with. "Background" is a
// pseudo-container corresponding to inactive launcher covered by another app.
@@ -216,11 +217,11 @@
// Launcher package. As during inproc tests the tested launcher may not be selected as the
// current launcher, choosing target package for inproc. For out-of-proc, use the installed
// launcher package.
- final String authorityPackage = testPackage.equals(targetPackage) ?
- getLauncherPackageName() :
- targetPackage;
+ mLauncherPackage = testPackage.equals(targetPackage)
+ ? getLauncherPackageName()
+ : targetPackage;
- String testProviderAuthority = authorityPackage + ".TestInfo";
+ String testProviderAuthority = mLauncherPackage + ".TestInfo";
mTestProviderUri = new Uri.Builder()
.scheme(ContentResolver.SCHEME_CONTENT)
.authority(testProviderAuthority)
@@ -450,7 +451,7 @@
}
}
- dumpDiagnostics();
+ dumpDiagnostics(message);
log("Hierarchy dump for: " + message);
dumpViewHierarchy();
@@ -458,10 +459,11 @@
return message;
}
- private void dumpDiagnostics() {
- Log.e("b/156287114", "Input:");
+ private void dumpDiagnostics(String message) {
+ log("Diagnostics for failure: " + message);
+ log("Input:");
logShellCommand("dumpsys input");
- Log.e("b/156287114", "TIS:");
+ log("TIS:");
logShellCommand("dumpsys activity service TouchInteractionService");
}
@@ -469,10 +471,10 @@
try {
for (String line : mDevice.executeShellCommand(command).split("\\n")) {
SystemClock.sleep(10);
- Log.d("b/156287114", line);
+ log(line);
}
} catch (IOException e) {
- Log.d("b/156287114", "Failed to execute " + command);
+ log("Failed to execute " + command);
}
}
@@ -628,6 +630,14 @@
fail("Launcher didn't initialize");
}
+ Parcelable executeAndWaitForLauncherEvent(Runnable command,
+ UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
+ return executeAndWaitForEvent(
+ command,
+ e -> mLauncherPackage.equals(e.getPackageName()) && eventFilter.accept(e),
+ message);
+ }
+
Parcelable executeAndWaitForEvent(Runnable command,
UiAutomation.AccessibilityEventFilter eventFilter, Supplier<String> message) {
try {
@@ -972,7 +982,7 @@
void runToState(Runnable command, int expectedState) {
final List<Integer> actualEvents = new ArrayList<>();
- executeAndWaitForEvent(
+ executeAndWaitForLauncherEvent(
command,
event -> isSwitchToStateEvent(event, expectedState, actualEvents),
() -> "Failed to receive an event for the state change: expected ["
@@ -1087,7 +1097,7 @@
return;
}
- executeAndWaitForEvent(
+ executeAndWaitForLauncherEvent(
() -> linearGesture(
startX, startY, endX, endY, steps, slowDown, GestureScope.INSIDE),
event -> TestProtocol.SCROLL_FINISHED_MESSAGE.equals(event.getClassName()),
@@ -1362,7 +1372,7 @@
if (mCheckEventsForSuccessfulGestures) {
final String message = eventChecker.verify(WAIT_TIME_MS, true);
if (message != null) {
- dumpDiagnostics();
+ dumpDiagnostics(message);
checkForAnomaly();
Assert.fail(formatSystemHealthMessage(
"http://go/tapl : successful gesture produced " + message));