Waiting until onResume before creating the wallpaper-open animation.
Bug: 77853906
Change-Id: I5126855492da59c9dbfef0ce2a97f94662a6522b
diff --git a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
index cd67300..c985354 100644
--- a/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
+++ b/quickstep/src/com/android/launcher3/LauncherAnimationRunner.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3;
+import static com.android.launcher3.Utilities.postAsyncCallback;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
@@ -28,16 +30,12 @@
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@TargetApi(Build.VERSION_CODES.P)
-public abstract class LauncherAnimationRunner extends AnimatorListenerAdapter
- implements RemoteAnimationRunnerCompat {
+public abstract class LauncherAnimationRunner implements RemoteAnimationRunnerCompat {
private static final int REFRESH_RATE_MS = 16;
private final Handler mHandler;
-
- private Runnable mSysFinishRunnable;
-
- private AnimatorSet mAnimator;
+ private AnimationResult mAnimationResult;
public LauncherAnimationRunner(Handler handler) {
mHandler = handler;
@@ -46,34 +44,26 @@
@BinderThread
@Override
public void onAnimationStart(RemoteAnimationTargetCompat[] targetCompats, Runnable runnable) {
- mHandler.post(() -> {
- // Finish any previous animation
- finishSystemAnimation();
-
- mSysFinishRunnable = runnable;
- mAnimator = getAnimator(targetCompats);
- if (mAnimator == null) {
- finishSystemAnimation();
- return;
- }
- mAnimator.addListener(this);
- mAnimator.start();
- // Because t=0 has the app icon in its original spot, we can skip the
- // first frame and have the same movement one frame earlier.
- mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
-
+ postAsyncCallback(mHandler, () -> {
+ finishExistingAnimation();
+ mAnimationResult = new AnimationResult(runnable);
+ onCreateAnimation(targetCompats, mAnimationResult);
});
}
+ /**
+ * Called on the UI thread when the animation targets are received. The implementation must
+ * call {@link AnimationResult#setAnimation(AnimatorSet)} with the target animation to be run.
+ */
@UiThread
- public abstract AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats);
+ public abstract void onCreateAnimation(
+ RemoteAnimationTargetCompat[] targetCompats, AnimationResult result);
@UiThread
- @Override
- public void onAnimationEnd(Animator animation) {
- if (animation == mAnimator) {
- mAnimator = null;
- finishSystemAnimation();
+ private void finishExistingAnimation() {
+ if (mAnimationResult != null) {
+ mAnimationResult.finish();
+ mAnimationResult = null;
}
}
@@ -83,20 +73,55 @@
@BinderThread
@Override
public void onAnimationCancelled() {
- mHandler.post(() -> {
- if (mAnimator != null) {
- mAnimator.removeListener(this);
- mAnimator.end();
- mAnimator = null;
- }
- });
+ postAsyncCallback(mHandler, this::finishExistingAnimation);
}
- @UiThread
- private void finishSystemAnimation() {
- if (mSysFinishRunnable != null) {
- mSysFinishRunnable.run();
- mSysFinishRunnable = null;
+ public static final class AnimationResult {
+
+ private final Runnable mFinishRunnable;
+
+ private AnimatorSet mAnimator;
+ private boolean mFinished = false;
+ private boolean mInitialized = false;
+
+ private AnimationResult(Runnable finishRunnable) {
+ mFinishRunnable = finishRunnable;
+ }
+
+ @UiThread
+ private void finish() {
+ if (!mFinished) {
+ mFinishRunnable.run();
+ mFinished = true;
+ }
+ }
+
+ @UiThread
+ public void setAnimation(AnimatorSet animation) {
+ if (mInitialized) {
+ throw new IllegalStateException("Animation already initialized");
+ }
+ mInitialized = true;
+ mAnimator = animation;
+ if (mAnimator == null) {
+ finish();
+ } else if (mFinished) {
+ // Animation callback was already finished, skip the animation.
+ mAnimator.end();
+ } else {
+ // Start the animation
+ mAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ finish();
+ }
+ });
+ mAnimator.start();
+
+ // Because t=0 has the app icon in its original spot, we can skip the
+ // first frame and have the same movement one frame earlier.
+ mAnimator.setCurrentPlayTime(REFRESH_RATE_MS);
+ }
}
}
}
\ No newline at end of file
diff --git a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 299e7d5..3ff9921 100644
--- a/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_APP_TRANSITIONS;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherState.NORMAL;
+import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
@@ -163,7 +164,8 @@
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mHandler) {
@Override
- public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ AnimationResult result) {
AnimatorSet anim = new AnimatorSet();
boolean launcherClosing =
@@ -185,7 +187,7 @@
anim.addListener(mForceInvisibleListener);
}
- return anim;
+ result.setAnimation(anim);
}
};
@@ -558,7 +560,16 @@
private RemoteAnimationRunnerCompat getWallpaperOpenRunner() {
return new LauncherAnimationRunner(mHandler) {
@Override
- public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ AnimationResult result) {
+ if (!mLauncher.hasBeenResumed()) {
+ // If launcher is not resumed, wait until new async-frame after resume
+ mLauncher.setOnResumeCallback(() ->
+ postAsyncCallback(mHandler, () ->
+ onCreateAnimation(targetCompats, result)));
+ return;
+ }
+
AnimatorSet anim = null;
RemoteAnimationProvider provider = mRemoteAnimationProvider;
if (provider != null) {
@@ -586,7 +597,7 @@
}
mLauncher.clearForceInvisibleFlag(INVISIBLE_ALL);
- return anim;
+ result.setAnimation(anim);
}
};
}
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 820875c..551984a 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -168,8 +168,9 @@
RemoteAnimationRunnerCompat runner = new LauncherAnimationRunner(mUiHandler) {
@Override
- public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
- return composeRecentsLaunchAnimator(taskView, targetCompats);
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ AnimationResult result) {
+ result.setAnimation(composeRecentsLaunchAnimator(taskView, targetCompats));
}
};
return ActivityOptionsCompat.makeRemoteAnimation(new RemoteAnimationAdapterCompat(
diff --git a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
index 2709523..d456367 100644
--- a/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
+++ b/quickstep/src/com/android/quickstep/WindowTransformSwipeHandler.java
@@ -16,13 +16,12 @@
package com.android.quickstep;
import static com.android.launcher3.BaseActivity.INVISIBLE_BY_STATE_HANDLER;
+import static com.android.launcher3.Utilities.postAsyncCallback;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.QuickScrubController.QUICK_SCRUB_START_DURATION;
import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
import static com.android.quickstep.TouchConsumer.INTERACTION_QUICK_SCRUB;
-import static com.android.systemui.shared.recents.utilities.Utilities
- .postAtFrontOfQueueAsynchronously;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
import android.animation.Animator;
@@ -248,7 +247,7 @@
if (Looper.myLooper() == handler.getLooper()) {
mStateCallback.setState(stateFlag);
} else {
- postAtFrontOfQueueAsynchronously(handler, () -> mStateCallback.setState(stateFlag));
+ postAsyncCallback(handler, () -> mStateCallback.setState(stateFlag));
}
}
diff --git a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
index 2ffcae3..7ee16c1 100644
--- a/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
+++ b/quickstep/src/com/android/quickstep/util/RemoteAnimationProvider.java
@@ -32,9 +32,11 @@
default ActivityOptions toActivityOptions(Handler handler, long duration) {
LauncherAnimationRunner runner = new LauncherAnimationRunner(handler) {
+
@Override
- public AnimatorSet getAnimator(RemoteAnimationTargetCompat[] targetCompats) {
- return createWindowAnimation(targetCompats);
+ public void onCreateAnimation(RemoteAnimationTargetCompat[] targetCompats,
+ AnimationResult result) {
+ result.setAnimation(createWindowAnimation(targetCompats));
}
};
return ActivityOptionsCompat.makeRemoteAnimation(
diff --git a/src/com/android/launcher3/BaseActivity.java b/src/com/android/launcher3/BaseActivity.java
index 1f1ef9a..f197921 100644
--- a/src/com/android/launcher3/BaseActivity.java
+++ b/src/com/android/launcher3/BaseActivity.java
@@ -56,8 +56,23 @@
protected UserEventDispatcher mUserEventDispatcher;
protected SystemUiController mSystemUiController;
- private boolean mStarted;
- private boolean mUserActive;
+ private static final int ACTIVITY_STATE_STARTED = 1 << 0;
+ private static final int ACTIVITY_STATE_RESUMED = 1 << 1;
+ /**
+ * State flag indicating if the user is active or the actitvity when to background as a result
+ * of user action.
+ * @see #isUserActive()
+ */
+ private static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 2;
+
+ @Retention(SOURCE)
+ @IntDef(
+ flag = true,
+ value = {ACTIVITY_STATE_STARTED, ACTIVITY_STATE_RESUMED, ACTIVITY_STATE_USER_ACTIVE})
+ public @interface ActivityFlags{}
+
+ @ActivityFlags
+ private int mActivityFlags;
// When the recents animation is running, the visibility of the Launcher is managed by the
// animation
@@ -103,19 +118,19 @@
@Override
protected void onStart() {
- mStarted = true;
+ mActivityFlags |= ACTIVITY_STATE_STARTED;
super.onStart();
}
@Override
protected void onResume() {
- mUserActive = true;
+ mActivityFlags |= ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE;
super.onResume();
}
@Override
protected void onUserLeaveHint() {
- mUserActive = false;
+ mActivityFlags &= ~ACTIVITY_STATE_USER_ACTIVE;
super.onUserLeaveHint();
}
@@ -129,17 +144,30 @@
@Override
protected void onStop() {
- mStarted = false;
+ mActivityFlags &= ~ACTIVITY_STATE_STARTED;
mForceInvisible = 0;
super.onStop();
}
+ @Override
+ protected void onPause() {
+ mActivityFlags &= ~ACTIVITY_STATE_RESUMED;
+ super.onPause();
+ }
+
public boolean isStarted() {
- return mStarted;
+ return (mActivityFlags & ACTIVITY_STATE_STARTED) != 0;
+ }
+
+ /**
+ * isResumed in already defined as a hidden final method in Activity.java
+ */
+ public boolean hasBeenResumed() {
+ return (mActivityFlags & ACTIVITY_STATE_RESUMED) != 0;
}
public boolean isUserActive() {
- return mUserActive;
+ return (mActivityFlags & ACTIVITY_STATE_USER_ACTIVE) != 0;
}
public void addOnDeviceProfileChangeListener(OnDeviceProfileChangeListener listener) {
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ba96d4a..98440ff 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -35,6 +35,8 @@
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
+import android.os.Handler;
+import android.os.Message;
import android.os.PowerManager;
import android.os.TransactionTooLargeException;
import android.support.v4.os.BuildCompat;
@@ -575,4 +577,12 @@
return hashSet;
}
+ /**
+ * Utility method to post a runnable on the handler, skipping the synchronization barriers.
+ */
+ public static void postAsyncCallback(Handler handler, Runnable callback) {
+ Message msg = Message.obtain(handler, callback);
+ msg.setAsynchronous(true);
+ handler.sendMessage(msg);
+ }
}