Merge "Log dismissing or launching recent tasks" into ub-launcher3-master
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index af81a59..a2567c8 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -57,6 +57,7 @@
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.quickstep.RecentsAnimationInterpolator;
import com.android.quickstep.RecentsAnimationInterpolator.TaskWindowBounds;
+import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task;
@@ -106,7 +107,7 @@
private DeviceProfile mDeviceProfile;
private View mFloatingView;
- private RemoteAnimationRunnerCompat mRemoteAnimationOverride;
+ private RemoteAnimationProvider mRemoteAnimationProvider;
private final AnimatorListenerAdapter mReapplyStateListener = new AnimatorListenerAdapter() {
@Override
@@ -179,8 +180,8 @@
return getDefaultActivityLaunchOptions(launcher, v);
}
- public void setRemoteAnimationOverride(RemoteAnimationRunnerCompat remoteAnimationOverride) {
- mRemoteAnimationOverride = remoteAnimationOverride;
+ public void setRemoteAnimationProvider(RemoteAnimationProvider animationProvider) {
+ mRemoteAnimationProvider = animationProvider;
}
/**
@@ -683,52 +684,33 @@
private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
return new LauncherAnimationRunner(mHandler) {
@Override
- public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats,
- Runnable runnable) {
- if (mLauncher.getStateManager().getState().overviewUi
- && mRemoteAnimationOverride != null) {
- // This transition is only used for the fallback activity and should not be
- // managed here (but necessary to implement here since the defined remote
- // animation currently takes precendence over the one defined in the activity
- // options).
- mRemoteAnimationOverride.onAnimationStart(targetCompats, runnable);
- return;
- }
- super.onAnimationStart(targetCompats, runnable);
- }
-
- @Override
- public void onAnimationCancelled() {
- if (mLauncher.getStateManager().getState().overviewUi
- && mRemoteAnimationOverride != null) {
- // This transition is only used for the fallback activity and should not be
- // managed here (but necessary to implement here since the defined remote
- // animation currently takes precendence over the one defined in the activity
- // options).
- mRemoteAnimationOverride.onAnimationCancelled();
- return;
- }
- super.onAnimationCancelled();
- }
-
- @Override
public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
- AnimatorSet anim = new AnimatorSet();
- anim.play(getClosingWindowAnimators(targetCompats));
-
- // Normally, we run the launcher content animation when we are transitioning home,
- // but if home is already visible, then we don't want to animate the contents of
- // launcher unless we know that we are animating home as a result of the home button
- // press with quickstep, which will result in launcher being started on touch down,
- // prior to the animation home (and won't be in the targets list because it is
- // already visible). In that case, we force invisibility on touch down, and only
- // reset it after the animation to home is initialized.
- if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
- || mLauncher.isForceInvisible()) {
- // Only register the content animation for cancellation when state changes
- mLauncher.getStateManager().setCurrentAnimation(anim);
- createLauncherResumeAnimation(anim);
+ AnimatorSet anim = null;
+ RemoteAnimationProvider provider = mRemoteAnimationProvider;
+ if (provider != null) {
+ anim = provider.createWindowAnimation(targetCompats);
}
+
+ if (anim == null) {
+ anim = new AnimatorSet();
+ anim.play(getClosingWindowAnimators(targetCompats));
+
+ // Normally, we run the launcher content animation when we are transitioning
+ // home, but if home is already visible, then we don't want to animate the
+ // contents of launcher unless we know that we are animating home as a result
+ // of the home button press with quickstep, which will result in launcher being
+ // started on touch down, prior to the animation home (and won't be in the
+ // targets list because it is already visible). In that case, we force
+ // invisibility on touch down, and only reset it after the animation to home
+ // is initialized.
+ if (launcherIsATargetWithMode(targetCompats, MODE_OPENING)
+ || mLauncher.isForceInvisible()) {
+ // Only register the content animation for cancellation when state changes
+ mLauncher.getStateManager().setCurrentAnimation(anim);
+ createLauncherResumeAnimation(anim);
+ }
+ }
+
mLauncher.setForceInvisible(false);
return anim;
}
diff --git a/quickstep/src/com/android/launcher3/LauncherInitListener.java b/quickstep/src/com/android/launcher3/LauncherInitListener.java
index 0d1038a..27f1698 100644
--- a/quickstep/src/com/android/launcher3/LauncherInitListener.java
+++ b/quickstep/src/com/android/launcher3/LauncherInitListener.java
@@ -15,13 +15,16 @@
*/
package com.android.launcher3;
-import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
-
import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
import com.android.launcher3.states.InternalStateHandler;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.util.RemoteAnimationProvider;
import java.util.function.BiPredicate;
@@ -30,15 +33,33 @@
private final BiPredicate<Launcher, Boolean> mOnInitListener;
+ private RemoteAnimationProvider mRemoteAnimationProvider;
+
public LauncherInitListener(BiPredicate<Launcher, Boolean> onInitListener) {
mOnInitListener = onInitListener;
}
@Override
protected boolean init(Launcher launcher, boolean alreadyOnHome) {
- // For the duration of the gesture, lock the screen orientation to ensure that we do not
- // rotate mid-quickscrub
- launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
+ if (mRemoteAnimationProvider != null) {
+ LauncherAppTransitionManagerImpl appTransitionManager =
+ (LauncherAppTransitionManagerImpl) launcher.getAppTransitionManager();
+
+ // Set a one-time animation provider. After the first call, this will get cleared.
+ // TODO: Probably also check the intended target id.
+ appTransitionManager.setRemoteAnimationProvider((targets) -> {
+
+ // On the first call clear the reference.
+ appTransitionManager.setRemoteAnimationProvider(null);
+ RemoteAnimationProvider provider = mRemoteAnimationProvider;
+ mRemoteAnimationProvider = null;
+
+ if (provider != null && launcher.getStateManager().getState().overviewUi) {
+ return provider.createWindowAnimation(targets);
+ }
+ return null;
+ });
+ }
return mOnInitListener.test(launcher, alreadyOnHome);
}
@@ -49,6 +70,18 @@
@Override
public void unregister() {
+ mRemoteAnimationProvider = null;
clearReference();
}
+
+ @Override
+ public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
+ Context context, Handler handler, long duration) {
+ mRemoteAnimationProvider = animProvider;
+
+ register();
+
+ Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
+ context.startActivity(addToIntent(new Intent((intent))), options);
+ }
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
index 355b88d..30ceb43 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/LandscapeStatesTouchController.java
@@ -60,12 +60,13 @@
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- if (fromState == ALL_APPS) {
+ if (fromState == ALL_APPS && !isDragTowardPositive) {
// Should swipe down go to OVERVIEW instead?
return TouchInteractionService.isConnected() ?
mLauncher.getStateManager().getLastState() : NORMAL;
- } else {
+ } else if (isDragTowardPositive) {
return ALL_APPS;
}
+ return fromState;
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
index d97b7b2..ec54732 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/OverviewState.java
@@ -110,7 +110,7 @@
@Override
public int getVisibleElements(Launcher launcher) {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
- return NONE;
+ return DRAG_HANDLE_INDICATOR;
} else {
return HOTSEAT_SEARCH_BOX | DRAG_HANDLE_INDICATOR |
(launcher.getAppsView().getFloatingHeaderView().hasVisibleContent()
diff --git a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
index 5971576..012b545 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/PortraitStatesTouchController.java
@@ -141,7 +141,7 @@
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- if (fromState == ALL_APPS) {
+ if (fromState == ALL_APPS && !isDragTowardPositive) {
// Should swipe down go to OVERVIEW instead?
return TouchInteractionService.isConnected() ?
mLauncher.getStateManager().getLastState() : NORMAL;
diff --git a/quickstep/src/com/android/quickstep/ActivityControlHelper.java b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
index 3e96c44..d82b8f4 100644
--- a/quickstep/src/com/android/quickstep/ActivityControlHelper.java
+++ b/quickstep/src/com/android/quickstep/ActivityControlHelper.java
@@ -22,7 +22,6 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
-import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
@@ -39,19 +38,17 @@
import com.android.launcher3.LauncherAppTransitionManagerImpl;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
-import com.android.launcher3.MainThreadExecutor;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.quickstep.fallback.FallbackRecentsView;
+import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.LauncherLayoutListener;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.AssistDataReceiver;
import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import java.util.function.BiPredicate;
@@ -85,9 +82,6 @@
void startRecentsFromSwipe(Intent intent, AssistDataReceiver assistDataReceiver,
final RecentsAnimationListener remoteAnimationListener);
- void startRecentsFromButton(Context context, Intent intent,
- RecentsAnimationListener remoteAnimationListener);
-
@UiThread
@Nullable
RecentsView getVisibleRecentsView();
@@ -214,24 +208,6 @@
intent, assistDataReceiver, remoteAnimationListener, null, null);
}
- @Override
- public void startRecentsFromButton(Context context, Intent intent,
- RecentsAnimationListener remoteAnimationListener) {
- // We should use the remove animation for the fallback activity recents button case,
- // it works better with PiP. In Launcher, we have already registered the remote
- // animation definition, which takes priority over explicitly defined remote
- // animations in the provided activity options when starting the activity, so we
- // just register a remote animation factory to get a callback to handle this.
- LauncherAppTransitionManagerImpl appTransitionManager =
- (LauncherAppTransitionManagerImpl) getLauncher().getAppTransitionManager();
- appTransitionManager.setRemoteAnimationOverride(new RecentsAnimationActivityOptions(
- remoteAnimationListener, () -> {
- // Once the controller is finished, also reset the remote animation override
- appTransitionManager.setRemoteAnimationOverride(null);
- }));
- context.startActivity(intent);
- }
-
@Nullable
@UiThread
private Launcher getLauncher() {
@@ -360,19 +336,6 @@
intent, assistDataReceiver, remoteAnimationListener, null, null);
}
- @Override
- public void startRecentsFromButton(Context context, Intent intent,
- RecentsAnimationListener remoteAnimationListener) {
- // We should use the remove animation for the fallback activity recents button case,
- // it works better with PiP. For the fallback activity, we should not have registered
- // the launcher app transition manager, so we should just start the remote animation here.
- ActivityOptions options = ActivityOptionsCompat.makeRemoteAnimation(
- new RemoteAnimationAdapterCompat(
- new RecentsAnimationActivityOptions(remoteAnimationListener, null),
- 10000, 10000));
- context.startActivity(intent, options.toBundle());
- }
-
@Nullable
@Override
public RecentsView getVisibleRecentsView() {
@@ -403,5 +366,8 @@
void register();
void unregister();
+
+ void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
+ Context context, Handler handler, long duration);
}
}
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index 8e59578..2fb4127 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -16,68 +16,46 @@
package com.android.quickstep;
import static com.android.launcher3.LauncherState.OVERVIEW;
+import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
-import android.animation.Animator;
+import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
-import android.app.ActivityManager.RecentTaskInfo;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
-import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Build;
-import android.os.Bundle;
import android.os.SystemClock;
-import android.os.UserHandle;
-import android.support.annotation.UiThread;
-import android.support.annotation.WorkerThread;
-import android.util.SparseArray;
+import android.util.Log;
+import android.view.View;
import android.view.ViewConfiguration;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.MainThreadExecutor;
-import com.android.launcher3.Utilities;
-import com.android.launcher3.anim.AnimationSuccessListener;
-import com.android.launcher3.anim.Interpolators;
+import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.states.InternalStateHandler;
-import com.android.launcher3.util.TraceHelper;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.SysuiEventLogger;
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.recents.view.AppTransitionAnimationSpecCompat;
-import com.android.systemui.shared.recents.view.AppTransitionAnimationSpecsFuture;
-import com.android.systemui.shared.recents.view.RecentsTransition;
import com.android.systemui.shared.system.ActivityManagerWrapper;
-import com.android.systemui.shared.system.AssistDataReceiver;
-import com.android.systemui.shared.system.BackgroundExecutor;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import java.util.Collections;
-import java.util.List;
-import java.util.function.Consumer;
-
/**
* Helper class to handle various atomic commands for switching between Overview.
*/
@TargetApi(Build.VERSION_CODES.P)
public class OverviewCommandHelper extends InternalStateHandler {
- private static final int RID_RESET_SWIPE_HANDLER = 0;
- private static final int RID_CANCEL_CONTROLLER = 1;
- private static final int RID_CANCEL_ZOOM_OUT_ANIMATION = 2;
-
private static final long RECENTS_LAUNCH_DURATION = 200;
private static final String TAG = "OverviewCommandHelper";
@@ -91,16 +69,7 @@
public final Intent homeIntent;
public final ComponentName launcher;
- private final SparseArray<Runnable> mCurrentCommandFinishRunnables = new SparseArray<>();
- // Monotonically increasing command ids.
- private int mCurrentCommandId = 0;
-
private long mLastToggleTime;
- private WindowTransformSwipeHandler mWindowTransformSwipeHandler;
-
- private final Point mWindowSize = new Point();
- private final Rect mTaskTargetRect = new Rect();
- private final RectF mTempTaskTargetRect = new RectF();
public OverviewCommandHelper(Context context) {
mContext = context;
@@ -132,112 +101,6 @@
initWhenReady();
}
- @UiThread
- private void addFinishCommand(int requestId, int id, Runnable action) {
- if (requestId < mCurrentCommandId) {
- action.run();
- } else {
- mCurrentCommandFinishRunnables.put(id, action);
- }
- }
-
- @UiThread
- private void clearFinishCommand(int requestId, int id) {
- if (requestId == mCurrentCommandId) {
- mCurrentCommandFinishRunnables.remove(id);
- }
- }
-
- @UiThread
- private void initSwipeHandler(ActivityControlHelper helper, long time,
- Consumer<WindowTransformSwipeHandler> onAnimationInitCallback) {
- final int commandId = mCurrentCommandId;
- final RunningTaskInfo runningTask = ActivityManagerWrapper.getInstance().getRunningTask();
- final int runningTaskId = runningTask.id;
- final WindowTransformSwipeHandler handler =
- new WindowTransformSwipeHandler(runningTask, mContext, time, helper);
-
- // Preload the plan
- mRecentsModel.loadTasks(runningTaskId, null);
- mWindowTransformSwipeHandler = handler;
-
- mTempTaskTargetRect.setEmpty();
- handler.setGestureEndCallback(() -> {
- if (mWindowTransformSwipeHandler == handler) {
- mWindowTransformSwipeHandler = null;
- mTempTaskTargetRect.setEmpty();
- }
- clearFinishCommand(commandId, RID_RESET_SWIPE_HANDLER);
- clearFinishCommand(commandId, RID_CANCEL_CONTROLLER);
- });
- handler.initWhenReady();
- addFinishCommand(commandId, RID_RESET_SWIPE_HANDLER, handler::reset);
-
- TraceHelper.beginSection(TAG);
- Runnable startActivity = () -> helper.startRecentsFromButton(mContext,
- addToIntent(homeIntent),
- new RecentsAnimationListener() {
- public void onAnimationStart(
- RecentsAnimationControllerCompat controller,
- RemoteAnimationTargetCompat[] apps, Rect homeContentInsets,
- Rect minimizedHomeBounds) {
- if (mWindowTransformSwipeHandler == handler) {
- TraceHelper.partitionSection(TAG, "Received");
- handler.onRecentsAnimationStart(controller, apps, homeContentInsets,
- minimizedHomeBounds);
- mTempTaskTargetRect.set(handler.getTargetRect(mWindowSize));
-
- ThumbnailData thumbnail = mAM.getTaskThumbnail(runningTaskId,
- true /* reducedResolution */);
- mMainThreadExecutor.execute(() -> {
- addFinishCommand(commandId,
- RID_CANCEL_CONTROLLER, () -> controller.finish(true));
- if (commandId == mCurrentCommandId) {
- onAnimationInitCallback.accept(handler);
-
- // The animation has started, which means the other activity
- // should be paused, lets update the thumbnail
- handler.switchToScreenshotImmediate(thumbnail);
- }
- });
- } else {
- TraceHelper.endSection(TAG, "Finishing no handler");
- controller.finish(false /* toHome */);
- }
- }
-
- public void onAnimationCanceled() {
- TraceHelper.endSection(TAG, "Cancelled: " + handler);
- if (mWindowTransformSwipeHandler == handler) {
- handler.onRecentsAnimationCanceled();
- }
- }
- });
-
- // We should almost always get touch-town on background thread. This is an edge case
- // when the background Choreographer has not yet initialized.
- BackgroundExecutor.get().submit(startActivity);
- }
-
- @UiThread
- private void startZoomOutAnim(final WindowTransformSwipeHandler handler) {
- final int commandId = mCurrentCommandId;
- ValueAnimator anim = ValueAnimator.ofInt(0, -handler.getTransitionLength());
- anim.addUpdateListener((a) -> handler.updateDisplacement((Integer) a.getAnimatedValue()));
- anim.addListener(new AnimationSuccessListener() {
- @Override
- public void onAnimationSuccess(Animator animator) {
- handler.onGestureEnded(0);
- clearFinishCommand(commandId, RID_CANCEL_ZOOM_OUT_ANIMATION);
- }
- });
- handler.onGestureStarted();
- anim.setDuration(RECENTS_LAUNCH_DURATION);
- anim.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
- anim.start();
- addFinishCommand(commandId, RID_CANCEL_ZOOM_OUT_ANIMATION, anim::cancel);
- }
-
public void onOverviewToggle() {
// If currently screen pinning, do not enter overview
if (ActivityManagerWrapper.getInstance().isScreenPinningActive()) {
@@ -245,45 +108,7 @@
}
ActivityManagerWrapper.getInstance().closeSystemWindows("recentapps");
- long time = SystemClock.elapsedRealtime();
- mMainThreadExecutor.execute(() -> {
- long elapsedTime = time - mLastToggleTime;
- mLastToggleTime = time;
-
- mCurrentCommandId++;
- mTempTaskTargetRect.round(mTaskTargetRect);
- int runnableCount = mCurrentCommandFinishRunnables.size();
- if (runnableCount > 0) {
- for (int i = 0; i < runnableCount; i++) {
- mCurrentCommandFinishRunnables.valueAt(i).run();
- }
- mCurrentCommandFinishRunnables.clear();
- }
-
- // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
- // the menu activity which takes window focus, prevening the right condition from
- // being run below
- ActivityControlHelper helper = getActivityControlHelper();
- RecentsView recents = helper.getVisibleRecentsView();
- if (recents != null) {
- // Launch the next task
- recents.showNextTask();
- } else {
- if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
- // The user tried to launch back into overview too quickly, either after
- // launching an app, or before overview has actually shown, just ignore for now
- return;
- }
-
- // Start overview
- if (helper.switchToRecentsIfVisible()) {
- SysuiEventLogger.writeDummyRecentsTransition(0);
- // Do nothing
- } else {
- initSwipeHandler(helper, time, this::startZoomOutAnim);
- }
- }
- });
+ mMainThreadExecutor.execute(new RecentsActivityCommand<>());
}
public void onOverviewShown() {
@@ -298,48 +123,6 @@
);
}
- public void onOverviewHidden() {
- getLauncher().runOnUiThread(() -> {
- if (isOverviewAlmostVisible()) {
- final RecentsView rv = getLauncher().getOverviewPanel();
- rv.launchNextTask();
- }
- }
- );
- }
-
- @WorkerThread
- private void startLastTask() {
- // TODO: This should go through recents model.
- List<RecentTaskInfo> tasks = mAM.getRecentTasks(2, UserHandle.myUserId());
- if (tasks.size() > 1) {
- RecentTaskInfo rti = tasks.get(1);
-
- final ActivityOptions options;
- if (!mTaskTargetRect.isEmpty()) {
- final Rect targetRect = new Rect(mTaskTargetRect);
- targetRect.offset(Utilities.isRtl(mContext.getResources())
- ? - mTaskTargetRect.width() : mTaskTargetRect.width(), 0);
- final AppTransitionAnimationSpecCompat specCompat =
- new AppTransitionAnimationSpecCompat(rti.id, null, targetRect);
- AppTransitionAnimationSpecsFuture specFuture =
- new AppTransitionAnimationSpecsFuture(mMainThreadExecutor.getHandler()) {
-
- @Override
- public List<AppTransitionAnimationSpecCompat> composeSpecs() {
- return Collections.singletonList(specCompat);
- }
- };
- options = RecentsTransition.createAspectScaleAnimation(mContext,
- mMainThreadExecutor.getHandler(), true /* scaleUp */,
- specFuture, () -> {});
- } else {
- options = ActivityOptions.makeBasic();
- }
- mAM.startActivityFromRecents(rti.id, options);
- }
- }
-
private boolean isOverviewAlmostVisible() {
if (clearReference()) {
return true;
@@ -370,4 +153,123 @@
return new LauncherActivityControllerHelper();
}
}
+
+ private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
+
+ private final ActivityControlHelper<T> mHelper;
+ private final long mCreateTime;
+ private final int mRunningTaskId;
+
+ private ActivityInitListener mListener;
+ private T mActivity;
+
+ public RecentsActivityCommand() {
+ mHelper = getActivityControlHelper();
+ mCreateTime = SystemClock.elapsedRealtime();
+ mRunningTaskId = ActivityManagerWrapper.getInstance().getRunningTask().id;
+
+ // Preload the plan
+ mRecentsModel.loadTasks(mRunningTaskId, null);
+ }
+
+ @Override
+ public void run() {
+ long elapsedTime = mCreateTime - mLastToggleTime;
+ mLastToggleTime = mCreateTime;
+
+ // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
+ // the menu activity which takes window focus, preventing the right condition from
+ // being run below
+ RecentsView recents = mHelper.getVisibleRecentsView();
+ if (recents != null) {
+ // Launch the next task
+ recents.showNextTask();
+ } else {
+ if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
+ // The user tried to launch back into overview too quickly, either after
+ // launching an app, or before overview has actually shown, just ignore for now
+ return;
+ }
+
+ // Start overview
+ if (mHelper.switchToRecentsIfVisible()) {
+ SysuiEventLogger.writeDummyRecentsTransition(0);
+ // Do nothing
+ } else {
+ mListener = mHelper.createActivityInitListener(this::onActivityReady);
+ mListener.registerAndStartActivity(homeIntent, this::createWindowAnimation,
+ mContext, mMainThreadExecutor.getHandler(), RECENTS_LAUNCH_DURATION);
+ }
+ }
+ }
+
+ private boolean onActivityReady(T activity, Boolean wasVisible) {
+ activity.<RecentsView>getOverviewPanel().setCurrentTask(mRunningTaskId);
+ AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
+ mHelper.prepareRecentsUI(activity, wasVisible);
+ if (wasVisible) {
+ AnimatorPlaybackController controller =
+ mHelper.createControllerForVisibleActivity(activity);
+ controller.dispatchOnStart();
+ ValueAnimator anim =
+ controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
+ anim.setInterpolator(FAST_OUT_SLOW_IN);
+ anim.start();
+ }
+ mActivity = activity;
+ return false;
+ }
+
+ private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+ if (mListener != null) {
+ mListener.unregister();
+ }
+ RemoteAnimationProvider.showOpeningTarget(targetCompats);
+ AnimatorSet anim = new AnimatorSet();
+ if (mActivity == null) {
+ Log.e(TAG, "Animation created, before activity");
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
+ return anim;
+ }
+
+ RemoteAnimationTargetCompat closingTarget = null;
+ // Use the top closing app to determine the insets for the animation
+ for (RemoteAnimationTargetCompat target : targetCompats) {
+ if (target.mode == MODE_CLOSING) {
+ closingTarget = target;
+ break;
+ }
+ }
+ if (closingTarget == null) {
+ Log.e(TAG, "No closing app");
+ anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
+ return anim;
+ }
+
+ final ClipAnimationHelper clipHelper = new ClipAnimationHelper();
+
+ // At this point, the activity is already started and laid-out. Get the home-bounds
+ // relative to the screen using the rootView of the activity.
+ int loc[] = new int[2];
+ View rootView = mActivity.getRootView();
+ rootView.getLocationOnScreen(loc);
+ Rect homeBounds = new Rect(loc[0], loc[1],
+ loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
+ clipHelper.updateSource(homeBounds, closingTarget);
+
+ Rect targetRect = new Rect();
+ mHelper.getSwipeUpDestinationAndLength(
+ mActivity.getDeviceProfile(), mActivity, targetRect);
+ clipHelper.updateTargetRect(targetRect);
+
+
+ ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
+ valueAnimator.setDuration(RECENTS_LAUNCH_DURATION).setInterpolator(FAST_OUT_SLOW_IN);
+ valueAnimator.addUpdateListener((v) -> {
+ clipHelper.applyTransform(targetCompats, (float) v.getAnimatedValue());
+ });
+ anim.play(valueAnimator);
+ return anim;
+ }
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
index 5bd606e..77f0e7a 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivityTracker.java
@@ -16,9 +16,14 @@
package com.android.quickstep;
import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.Intent;
import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.util.RemoteAnimationProvider;
import java.lang.ref.WeakReference;
import java.util.function.BiPredicate;
@@ -78,4 +83,13 @@
return sCurrentActivity.get();
}
}
+
+ @Override
+ public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
+ Context context, Handler handler, long duration) {
+ register();
+
+ Bundle options = animProvider.toActivityOptions(handler, duration).toBundle();
+ context.startActivity(intent, options);
+ }
}
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java b/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java
deleted file mode 100644
index a25e192..0000000
--- a/quickstep/src/com/android/quickstep/RecentsAnimationActivityOptions.java
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright (C) 2018 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 android.graphics.Rect;
-
-import com.android.systemui.shared.recents.model.ThumbnailData;
-import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
-import com.android.systemui.shared.system.RecentsAnimationListener;
-import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.TransactionCompat;
-import com.android.systemui.shared.system.WindowManagerWrapper;
-import java.util.function.Consumer;
-
-/**
- * Class to create activity options to emulate recents transition.
- */
-public class RecentsAnimationActivityOptions implements RemoteAnimationRunnerCompat {
-
- private final RecentsAnimationListener mListener;
- private final Runnable mFinishCallback;
-
- public RecentsAnimationActivityOptions(RecentsAnimationListener listener,
- Runnable finishCallback) {
- mListener = listener;
- mFinishCallback = finishCallback;
- }
-
- @Override
- public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats,
- Runnable runnable) {
- showOpeningTarget(targetCompats);
- RemoteRecentsAnimationControllerCompat dummyRecentsAnim =
- new RemoteRecentsAnimationControllerCompat(() -> {
- runnable.run();
- if (mFinishCallback != null) {
- mFinishCallback.run();
- }
- });
-
- Rect insets = new Rect();
- WindowManagerWrapper.getInstance().getStableInsets(insets);
- mListener.onAnimationStart(dummyRecentsAnim, targetCompats, insets, null);
- }
-
- @Override
- public void onAnimationCancelled() {
- mListener.onAnimationCanceled();
- }
-
- private void showOpeningTarget(RemoteAnimationTargetCompat[] targetCompats) {
- TransactionCompat t = new TransactionCompat();
- for (RemoteAnimationTargetCompat target : targetCompats) {
- int layer = target.mode == RemoteAnimationTargetCompat.MODE_CLOSING
- ? Integer.MAX_VALUE
- : target.prefixOrderIndex;
- t.setLayer(target.leash, layer);
- t.show(target.leash);
- }
- t.apply();
- }
-
- private class RemoteRecentsAnimationControllerCompat extends RecentsAnimationControllerCompat {
-
- final Runnable mFinishCallback;
-
- public RemoteRecentsAnimationControllerCompat(Runnable finishCallback) {
- mFinishCallback = finishCallback;
- }
-
- @Override
- public ThumbnailData screenshotTask(int taskId) {
- return new ThumbnailData();
- }
-
- @Override
- public void setInputConsumerEnabled(boolean enabled) { }
-
- @Override
- public void setAnimationTargetsBehindSystemBars(boolean behindSystemBars) { }
-
- @Override
- public void finish(boolean toHome) {
- // This should never be called with toHome == false
- if (mFinishCallback != null) {
- mFinishCallback.run();
- }
- }
- }
-}
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index f6cf85a..d4c35e0 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -30,11 +30,8 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.Matrix;
-import android.graphics.Matrix.ScaleToFit;
import android.graphics.Point;
import android.graphics.Rect;
-import android.graphics.RectF;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
@@ -64,11 +61,11 @@
import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
import com.android.quickstep.ActivityControlHelper.LayoutListener;
import com.android.quickstep.TouchConsumer.InteractionType;
+import com.android.quickstep.util.ClipAnimationHelper;
import com.android.quickstep.util.SysuiEventLogger;
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.recents.utilities.RectFEvaluator;
import com.android.systemui.shared.system.InputConsumerController;
import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -132,26 +129,8 @@
private static final float MIN_PROGRESS_FOR_OVERVIEW = 0.5f;
- // The bounds of the source app in device coordinates
- private final Rect mSourceStackBounds = new Rect();
- // The insets of the source app
- private final Rect mSourceInsets = new Rect();
- // The source app bounds with the source insets applied, in the source app window coordinates
- private final RectF mSourceRect = new RectF();
- // The bounds of the task view in launcher window coordinates
- private final RectF mTargetRect = new RectF();
- // Doesn't change after initialized, used as an anchor when changing mTargetRect
- private final RectF mInitialTargetRect = new RectF();
- // The insets to be used for clipping the app window, which can be larger than mSourceInsets
- // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
- // app window coordinates.
- private final RectF mSourceWindowClipInsets = new RectF();
+ private final ClipAnimationHelper mClipAnimationHelper = new ClipAnimationHelper();
- // The bounds of launcher (not including insets) in device coordinates
- private final Rect mHomeStackBounds = new Rect();
- // The clip rect in source app window coordinates
- private final Rect mClipRect = new Rect();
- private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
protected Runnable mGestureEndCallback;
protected boolean mIsGoingToHome;
private DeviceProfile mDp;
@@ -192,14 +171,9 @@
InputConsumerController.getRecentsAnimationInputConsumer();
private final RecentsAnimationWrapper mRecentsAnimationWrapper = new RecentsAnimationWrapper();
- private Matrix mTmpMatrix = new Matrix();
private final long mTouchTimeMs;
private long mLauncherFrameDrawnTime;
- // Only used with the recents activity, when the screenshot should be fetched at the beginning
- // of the animation and not at the end when the activity is already paused
- private boolean mSkipScreenshotAtEndOfTransition;
-
WindowTransformSwipeHandler(RunningTaskInfo runningTaskInfo, Context context, long touchTimeMs,
ActivityControlHelper<T> controller) {
mContext = context;
@@ -273,42 +247,11 @@
private void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
- mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
- mSourceStackBounds.width() - mSourceInsets.right,
- mSourceStackBounds.height() - mSourceInsets.bottom);
Rect tempRect = new Rect();
mTransitionDragLength = mActivityControlHelper
.getSwipeUpDestinationAndLength(dp, mContext, tempRect);
-
- mTargetRect.set(tempRect);
- mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
- mHomeStackBounds.top - mSourceStackBounds.top);
- mInitialTargetRect.set(mTargetRect);
-
- // Calculate the clip based on the target rect (since the content insets and the
- // launcher insets may differ, so the aspect ratio of the target rect can differ
- // from the source rect. The difference between the target rect (scaled to the
- // source rect) is the amount to clip on each edge.
- RectF scaledTargetRect = new RectF(mTargetRect);
- Utilities.scaleRectFAboutCenter(scaledTargetRect,
- mSourceRect.width() / mTargetRect.width());
- scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top);
- mSourceWindowClipInsets.set(
- Math.max(scaledTargetRect.left, 0),
- Math.max(scaledTargetRect.top, 0),
- Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
- Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
- mSourceRect.set(scaledTargetRect);
- }
-
- public int getTransitionLength() {
- return mTransitionDragLength;
- }
-
- public RectF getTargetRect(Point outWindowSize) {
- outWindowSize.set(mDp.widthPx, mDp.heightPx);
- return mInitialTargetRect;
+ mClipAnimationHelper.updateTargetRect(tempRect);
}
private long getFadeInDuration() {
@@ -482,40 +425,10 @@
synchronized (mRecentsAnimationWrapper) {
if (mRecentsAnimationWrapper.controller != null) {
- RectF currentRect;
- synchronized (mTargetRect) {
- Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB
- ? ACCEL_2 : LINEAR;
- float interpolated = interpolator.getInterpolation(shift);
- currentRect = mRectFEvaluator.evaluate(interpolated, mSourceRect, mTargetRect);
- // Stay lined up with the center of the target, since it moves for quick scrub.
- currentRect.offset(mTargetRect.centerX() - currentRect.centerX(), 0);
- }
-
- mClipRect.left = (int) (mSourceWindowClipInsets.left * shift);
- mClipRect.top = (int) (mSourceWindowClipInsets.top * shift);
- mClipRect.right = (int)
- (mSourceStackBounds.width() - (mSourceWindowClipInsets.right * shift));
- mClipRect.bottom = (int)
- (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * shift));
-
- mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
-
- TransactionCompat transaction = new TransactionCompat();
- for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
- if (app.mode == MODE_CLOSING) {
- mTmpMatrix.postTranslate(app.position.x, app.position.y);
- transaction.setMatrix(app.leash, mTmpMatrix)
- .setWindowCrop(app.leash, mClipRect);
-
- if (app.isNotInRecents) {
- transaction.setAlpha(app.leash, 1 - shift);
- }
-
- transaction.show(app.leash);
- }
- }
- transaction.apply();
+ Interpolator interpolator = mInteractionType == INTERACTION_QUICK_SCRUB
+ ? ACCEL_2 : LINEAR;
+ float interpolated = interpolator.getInterpolation(shift);
+ mClipAnimationHelper.applyTransform(mRecentsAnimationWrapper.targets, interpolated);
}
}
@@ -533,13 +446,9 @@
if (firstTask != null) {
int scrollForFirstTask = mRecentsView.getScrollForPage(0);
int offsetFromFirstTask = (scrollForFirstTask - mRecentsView.getScrollX());
- synchronized (mTargetRect) {
- mTargetRect.set(mInitialTargetRect);
- Utilities.scaleRectFAboutCenter(mTargetRect, firstTask.getScaleX());
- float offsetX = offsetFromFirstTask + firstTask.getTranslationX();
- float offsetY = mRecentsView.getTranslationY();
- mTargetRect.offset(offsetX, offsetY);
- }
+ mClipAnimationHelper.offsetTarget(firstTask.getScaleX(),
+ offsetFromFirstTask + firstTask.getTranslationX(),
+ mRecentsView.getTranslationY());
}
if (mRecentsAnimationWrapper.controller != null) {
// TODO: This logic is spartanic!
@@ -564,13 +473,15 @@
for (RemoteAnimationTargetCompat target : apps) {
if (target.mode == MODE_CLOSING) {
DeviceProfile dp = LauncherAppState.getIDP(mContext).getDeviceProfile(mContext);
+ final Rect homeStackBounds;
+
if (minimizedHomeBounds != null) {
- mHomeStackBounds.set(minimizedHomeBounds);
+ homeStackBounds = minimizedHomeBounds;
dp = dp.getMultiWindowProfile(mContext,
new Point(minimizedHomeBounds.width(), minimizedHomeBounds.height()));
dp.updateInsets(homeContentInsets);
} else {
- mHomeStackBounds.set(new Rect(0, 0, dp.widthPx, dp.heightPx));
+ homeStackBounds = new Rect(0, 0, dp.widthPx, dp.heightPx);
// TODO: Workaround for an existing issue where the home content insets are
// not valid immediately after rotation, just use the stable insets for now
Rect insets = new Rect();
@@ -578,16 +489,7 @@
dp.updateInsets(insets);
}
- // Initialize the start and end animation bounds
- // TODO: Remove once platform is updated
- try {
- mSourceInsets.set(target.getContentInsets());
- } catch (Error e) {
- // TODO: Remove once platform is updated, use stable insets as fallback
- WindowManagerWrapper.getInstance().getStableInsets(mSourceInsets);
- }
- mSourceStackBounds.set(target.sourceContainerBounds);
-
+ mClipAnimationHelper.updateSource(homeStackBounds, target);
initTransitionEndpoints(dp);
}
}
@@ -736,7 +638,7 @@
};
synchronized (mRecentsAnimationWrapper) {
- if (mRecentsAnimationWrapper.controller != null && !mSkipScreenshotAtEndOfTransition) {
+ if (mRecentsAnimationWrapper.controller != null) {
TransactionCompat transaction = new TransactionCompat();
for (RemoteAnimationTargetCompat app : mRecentsAnimationWrapper.targets) {
if (app.mode == MODE_CLOSING) {
@@ -745,6 +647,7 @@
mRecentsAnimationWrapper.controller.screenshotTask(app.taskId);
TaskView taskView = mRecentsView.updateThumbnail(app.taskId, thumbnail);
if (taskView != null) {
+ taskView.setAlpha(1);
// Defer finishing the animation until the next launcher frame with the
// new thumbnail
mActivityControlHelper.executeOnNextDraw(mActivity, taskView,
@@ -763,12 +666,6 @@
doLogGesture(true /* toLauncher */);
}
- @UiThread
- public void switchToScreenshotImmediate(ThumbnailData thumbnail) {
- mRecentsView.updateThumbnail(mRunningTaskId, thumbnail);
- mSkipScreenshotAtEndOfTransition = true;
- }
-
private void setupLauncherUiAfterSwipeUpAnimation() {
if (mLauncherTransitionController != null) {
mLauncherTransitionController.getAnimationPlayer().end();
diff --git a/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
new file mode 100644
index 0000000..0f895b8
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/ClipAnimationHelper.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2018 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 com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
+
+import android.graphics.Matrix;
+import android.graphics.Matrix.ScaleToFit;
+import android.graphics.Rect;
+import android.graphics.RectF;
+
+import com.android.launcher3.Utilities;
+import com.android.systemui.shared.recents.utilities.RectFEvaluator;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+
+/**
+ * Utility class to handle window clip animation
+ */
+public class ClipAnimationHelper {
+
+ // The bounds of the source app in device coordinates
+ private final Rect mSourceStackBounds = new Rect();
+ // The insets of the source app
+ private final Rect mSourceInsets = new Rect();
+ // The source app bounds with the source insets applied, in the source app window coordinates
+ private final RectF mSourceRect = new RectF();
+ // The bounds of the task view in launcher window coordinates
+ private final RectF mTargetRect = new RectF();
+ // Doesn't change after initialized, used as an anchor when changing mTargetRect
+ private final RectF mInitialTargetRect = new RectF();
+ // The insets to be used for clipping the app window, which can be larger than mSourceInsets
+ // if the aspect ratio of the target is smaller than the aspect ratio of the source rect. In
+ // app window coordinates.
+ private final RectF mSourceWindowClipInsets = new RectF();
+
+ // The bounds of launcher (not including insets) in device coordinates
+ public final Rect mHomeStackBounds = new Rect();
+
+ // The clip rect in source app window coordinates
+ private final Rect mClipRect = new Rect();
+ private final RectFEvaluator mRectFEvaluator = new RectFEvaluator();
+ private final Matrix mTmpMatrix = new Matrix();
+
+
+ public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
+ mHomeStackBounds.set(homeStackBounds);
+ mSourceInsets.set(target.getContentInsets());
+ mSourceStackBounds.set(target.sourceContainerBounds);
+
+ // TODO: Should sourceContainerBounds already have this offset?
+ mSourceStackBounds.offsetTo(target.position.x, target.position.y);
+ }
+
+ public void updateTargetRect(Rect targetRect) {
+ mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
+ mSourceStackBounds.width() - mSourceInsets.right,
+ mSourceStackBounds.height() - mSourceInsets.bottom);
+ mTargetRect.set(targetRect);
+ mTargetRect.offset(mHomeStackBounds.left - mSourceStackBounds.left,
+ mHomeStackBounds.top - mSourceStackBounds.top);
+
+ mInitialTargetRect.set(mTargetRect);
+
+ // Calculate the clip based on the target rect (since the content insets and the
+ // launcher insets may differ, so the aspect ratio of the target rect can differ
+ // from the source rect. The difference between the target rect (scaled to the
+ // source rect) is the amount to clip on each edge.
+ RectF scaledTargetRect = new RectF(mTargetRect);
+ Utilities.scaleRectFAboutCenter(scaledTargetRect,
+ mSourceRect.width() / mTargetRect.width());
+ scaledTargetRect.offsetTo(mSourceRect.left, mSourceRect.top);
+ mSourceWindowClipInsets.set(
+ Math.max(scaledTargetRect.left, 0),
+ Math.max(scaledTargetRect.top, 0),
+ Math.max(mSourceStackBounds.width() - scaledTargetRect.right, 0),
+ Math.max(mSourceStackBounds.height() - scaledTargetRect.bottom, 0));
+ mSourceRect.set(scaledTargetRect);
+ }
+
+ public void applyTransform(RemoteAnimationTargetCompat[] targets, float progress) {
+ RectF currentRect;
+ synchronized (mTargetRect) {
+ currentRect = mRectFEvaluator.evaluate(progress, mSourceRect, mTargetRect);
+ // Stay lined up with the center of the target, since it moves for quick scrub.
+ currentRect.offset(mTargetRect.centerX() - currentRect.centerX(), 0);
+ }
+
+ mClipRect.left = (int) (mSourceWindowClipInsets.left * progress);
+ mClipRect.top = (int) (mSourceWindowClipInsets.top * progress);
+ mClipRect.right = (int)
+ (mSourceStackBounds.width() - (mSourceWindowClipInsets.right * progress));
+ mClipRect.bottom = (int)
+ (mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress));
+
+ mTmpMatrix.setRectToRect(mSourceRect, currentRect, ScaleToFit.FILL);
+
+ TransactionCompat transaction = new TransactionCompat();
+ for (RemoteAnimationTargetCompat app : targets) {
+ if (app.mode == MODE_CLOSING) {
+ mTmpMatrix.postTranslate(app.position.x, app.position.y);
+ transaction.setMatrix(app.leash, mTmpMatrix)
+ .setWindowCrop(app.leash, mClipRect);
+ if (app.isNotInRecents) {
+ transaction.setAlpha(app.leash, 1 - progress);
+ }
+
+ transaction.show(app.leash);
+ }
+ }
+ transaction.apply();
+ }
+
+ public void offsetTarget(float scale, float offsetX, float offsetY) {
+ synchronized (mTargetRect) {
+ mTargetRect.set(mInitialTargetRect);
+ Utilities.scaleRectFAboutCenter(mTargetRect, scale);
+ mTargetRect.offset(offsetX, offsetY);
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
new file mode 100644
index 0000000..2ffcae3
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2018 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 android.animation.AnimatorSet;
+import android.app.ActivityOptions;
+import android.os.Handler;
+
+import com.android.launcher3.LauncherAnimationRunner;
+import com.android.systemui.shared.system.ActivityOptionsCompat;
+import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.TransactionCompat;
+
+@FunctionalInterface
+public interface RemoteAnimationProvider {
+
+ AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targets);
+
+ default ActivityOptions toActivityOptions(Handler handler, long duration) {
+ LauncherAnimationRunner runner = new LauncherAnimationRunner(handler) {
+ @Override
+ public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+ return createWindowAnimation(targetCompats);
+ }
+ };
+ return ActivityOptionsCompat.makeRemoteAnimation(
+ new RemoteAnimationAdapterCompat(runner, duration, 0));
+ }
+
+ static void showOpeningTarget(RemoteAnimationTargetCompat[] targetCompats) {
+ TransactionCompat t = new TransactionCompat();
+ for (RemoteAnimationTargetCompat target : targetCompats) {
+ int layer = target.mode == RemoteAnimationTargetCompat.MODE_CLOSING
+ ? Integer.MAX_VALUE
+ : target.prefixOrderIndex;
+ t.setLayer(target.leash, layer);
+ t.show(target.leash);
+ }
+ t.apply();
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
index 6b7143d..ac34d90 100644
--- a/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
+++ b/quickstep/src/com/android/quickstep/views/LauncherLayoutListener.java
@@ -15,6 +15,7 @@
*/
package com.android.quickstep.views;
+import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import static com.android.launcher3.states.RotationHelper.REQUEST_NONE;
import android.graphics.Rect;
@@ -39,6 +40,10 @@
super(launcher, null);
mLauncher = launcher;
setVisibility(INVISIBLE);
+
+ // For the duration of the gesture, lock the screen orientation to ensure that we do not
+ // rotate mid-quickscrub
+ launcher.getRotationHelper().setStateHandlerRequest(REQUEST_LOCK);
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index b22e2b6..6997ec4 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -133,13 +133,7 @@
private final TaskStackChangeListener mTaskStackListener = new TaskStackChangeListener() {
@Override
public void onTaskSnapshotChanged(int taskId, ThumbnailData snapshot) {
- for (int i = 0; i < getChildCount(); i++) {
- final TaskView taskView = (TaskView) getChildAt(i);
- if (taskView.getTask().key.id == taskId) {
- taskView.getThumbnail().setThumbnail(taskView.getTask(), snapshot);
- return;
- }
- }
+ updateThumbnail(taskId, snapshot);
}
@Override
@@ -241,7 +235,6 @@
final TaskView taskView = (TaskView) getChildAt(i);
if (taskView.getTask().key.id == taskId) {
taskView.onTaskDataLoaded(taskView.getTask(), thumbnailData);
- taskView.setAlpha(1);
return taskView;
}
}
@@ -604,12 +597,7 @@
new ActivityManager.TaskDescription(), 0, new ComponentName("", ""), false);
taskView.bind(mTmpRunningTask);
}
-
- mRunningTaskId = runningTaskId;
- setCurrentPage(0);
-
- // Load the tasks (if the loading is already
- mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
+ setCurrentTask(mRunningTaskId);
// Hide the task that we are animating into, ignore if there is no associated task (ie. the
// assistant)
@@ -618,6 +606,17 @@
}
}
+ /**
+ * Similar to {@link #showTask(int)} but does not put any restrictions on the first tile.
+ */
+ public void setCurrentTask(int runningTaskId) {
+ mRunningTaskId = runningTaskId;
+ setCurrentPage(0);
+
+ // Load the tasks (if the loading is already
+ mLoadPlanId = mModel.loadTasks(runningTaskId, this::applyLoadPlan);
+ }
+
public void showNextTask() {
TaskView runningTaskView = getTaskView(mRunningTaskId);
if (runningTaskView == null) {
@@ -1079,7 +1078,10 @@
@Override
protected void notifyPageSwitchListener(int prevPage) {
super.notifyPageSwitchListener(prevPage);
- getChildAt(mCurrentPage).sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ View currChild = getChildAt(mCurrentPage);
+ if (currChild != null) {
+ currChild.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
}
@Override
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index c04f825..f04acaf 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -148,7 +148,10 @@
mSnapshotView.setThumbnail(task, thumbnailData);
mIconView.setImageDrawable(task.icon);
mIconView.setOnClickListener(icon -> TaskMenuView.showForTask(this));
- mIconView.setOnLongClickListener(icon -> TaskMenuView.showForTask(this));
+ mIconView.setOnLongClickListener(icon -> {
+ requestDisallowInterceptTouchEvent(true);
+ return TaskMenuView.showForTask(this);
+ });
}
@Override
diff --git a/res/drawable/ic_close.xml b/res/drawable/ic_close.xml
index fc9ed49..8b2f55f 100644
--- a/res/drawable/ic_close.xml
+++ b/res/drawable/ic_close.xml
@@ -19,5 +19,5 @@
android:viewportWidth="24.0">
<path
android:fillColor="@android:color/white"
- android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+ android:pathData="M19,6.41L17.59,5L12,10.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L12,13.41L17.59,19L19,17.59L13.41,12L19,6.41z" />
</vector>
\ No newline at end of file
diff --git a/res/drawable/ic_uninstall_no_shadow.xml b/res/drawable/ic_uninstall_no_shadow.xml
index 2a86e10..37632d1 100644
--- a/res/drawable/ic_uninstall_no_shadow.xml
+++ b/res/drawable/ic_uninstall_no_shadow.xml
@@ -21,6 +21,11 @@
android:tint="?android:attr/textColorPrimary" >
<path
android:fillColor="#FFFFFFFF"
- android:pathData="M6,19c0,1.1,0.9,2,2,2h8c1.1,0,2-0.9,2-2V7H6V19z M18,4h-2.5l-0.71-0.71C14.61,3.11,14.35,3,14.09,
- 3H9.9C9.64,3,9.38,3.11,9.2,3.29L8.49,4h-2.5c-0.55,0-1,0.45-1,1s0.45,1,1,1h12c0.55,0,1-0.45,1-1C19,4.45,18.55,4,18,4L18,4z"/>
+ android:pathData="M15,4V3H9v1H4v2h1v13c0,1.1,0.9,2,2,2h10c1.1,0,2-0.9,2-2V6h1V4H15z M17,19H7V6h10V19z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 9 8 H 11 V 17 H 9 V 8 Z" />
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M 13 8 H 15 V 17 H 13 V 8 Z" />
</vector>
diff --git a/src/com/android/launcher3/ItemInfoWithIcon.java b/src/com/android/launcher3/ItemInfoWithIcon.java
index bf985c3..4677d31 100644
--- a/src/com/android/launcher3/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/ItemInfoWithIcon.java
@@ -91,6 +91,11 @@
public static final int FLAG_ADAPTIVE_ICON = 1 << 8;
/**
+ * Flag indicating that the icon is badged.
+ */
+ public static final int FLAG_ICON_BADGED = 1 << 9;
+
+ /**
* Status associated with the system state of the underlying item. This is calculated every
* time a new info is created and not persisted on the disk.
*/
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b410f4f..90c55c9 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -294,6 +294,7 @@
mPopupDataProvider = new PopupDataProvider(this);
mRotationHelper = new RotationHelper(this);
+ mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
boolean internalStateHandled = InternalStateHandler.handleCreate(this, getIntent());
if (internalStateHandled) {
@@ -341,8 +342,6 @@
getSystemUiController().updateUiState(SystemUiController.UI_STATE_BASE_WINDOW,
Themes.getAttrBoolean(this, R.attr.isWorkspaceDarkText));
- mAppTransitionManager = LauncherAppTransitionManager.newInstance(this);
-
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onCreate(savedInstanceState);
}
diff --git a/src/com/android/launcher3/dragndrop/DragView.java b/src/com/android/launcher3/dragndrop/DragView.java
index 8d4f2ef..e1e1f83 100644
--- a/src/com/android/launcher3/dragndrop/DragView.java
+++ b/src/com/android/launcher3/dragndrop/DragView.java
@@ -46,6 +46,7 @@
import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.ItemInfo;
+import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.LauncherAppState;
@@ -69,6 +70,8 @@
import java.util.Arrays;
import java.util.List;
+import static com.android.launcher3.ItemInfoWithIcon.FLAG_ICON_BADGED;
+
public class DragView extends View {
private static final ColorMatrix sTempMatrix1 = new ColorMatrix();
private static final ColorMatrix sTempMatrix2 = new ColorMatrix();
@@ -364,7 +367,10 @@
private Drawable getBadge(ItemInfo info, LauncherAppState appState, Object obj) {
int iconSize = appState.getInvariantDeviceProfile().iconBitmapSize;
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT) {
- if (info.id == ItemInfo.NO_ID || !(obj instanceof ShortcutInfoCompat)) {
+ boolean iconBadged = (info instanceof ItemInfoWithIcon)
+ && (((ItemInfoWithIcon) info).runtimeStatusFlags & FLAG_ICON_BADGED) > 0;
+ if ((info.id == ItemInfo.NO_ID && !iconBadged)
+ || !(obj instanceof ShortcutInfoCompat)) {
// The item is not yet added on home screen.
return new FixedSizeEmptyDrawable(iconSize);
}
diff --git a/src/com/android/launcher3/popup/PopupContainerWithArrow.java b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
index 454fc7d..b522b55 100644
--- a/src/com/android/launcher3/popup/PopupContainerWithArrow.java
+++ b/src/com/android/launcher3/popup/PopupContainerWithArrow.java
@@ -517,6 +517,12 @@
}
@Override
+ protected void closeComplete() {
+ super.closeComplete();
+ mOriginalIcon.setTextVisibility(mOriginalIcon.shouldTextBeVisible());
+ }
+
+ @Override
public boolean onTouch(View v, MotionEvent ev) {
// Touched a shortcut, update where it was touched so we can drag from there on long click.
switch (ev.getAction()) {
diff --git a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
index c875cf9..f1195ed 100644
--- a/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
+++ b/src/com/android/launcher3/touch/AbstractStateChangeTouchController.java
@@ -124,6 +124,10 @@
return mLauncher.getAllAppsController().getShiftRange();
}
+ /**
+ * Returns the state to go to from fromState given the drag direction. If there is no state in
+ * that direction, returns fromState.
+ */
protected abstract LauncherState getTargetState(LauncherState fromState,
boolean isDragTowardPositive);
diff --git a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
index c97c3cc..d1cddc1 100644
--- a/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
+++ b/src_ui_overrides/com/android/launcher3/uioverrides/AllAppsSwipeController.java
@@ -54,7 +54,12 @@
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
- return fromState == ALL_APPS ? NORMAL : ALL_APPS;
+ if (fromState == NORMAL && isDragTowardPositive) {
+ return ALL_APPS;
+ } else if (fromState == ALL_APPS && !isDragTowardPositive) {
+ return NORMAL;
+ }
+ return fromState;
}
@Override