From 7b088c3e2820809437ad40e88a457c220943692e Mon Sep 17 00:00:00 2001 From: Winson Chung Date: Wed, 19 Jul 2017 14:31:32 -0700 Subject: [PATCH] Fixing occasional jump when launching certain apps from Recents. - Removing code that cleared the pending options for an activity when it is fetched by the app. - Only handle the first window-animation-started callback from the system when transitioning out of Recents. - Add logic in Recents to disable toggling Recents while an activity transition has been requested (ie. activity started) and the transition actually starting. This prevents some cases where interleaving of the transition updates can occur while the app takes a long time to draw when it is resumed which could cause animation jumps and weirdness. Bug: 37277853 Test: Launch between apps quickly, especially those with camera. Change-Id: I7ff8a83539334a39b0ae049d267d1ab14a38e2f6 --- .../recents/IRecentsSystemUserCallbacks.aidl | 1 + .../com/android/systemui/recents/Recents.java | 20 +++++++ .../android/systemui/recents/RecentsImpl.java | 45 ++++++++++++-- .../systemui/recents/RecentsSystemUser.java | 7 +++ .../SetWaitingForTransitionStartEvent.java | 31 ++++++++++ .../views/RecentsTransitionHelper.java | 60 ++++++++++++++----- .../server/am/ActivityManagerService.java | 1 - 7 files changed, 144 insertions(+), 21 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java diff --git a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl index 1240e055d25ae..cc7798e8721b4 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl +++ b/packages/SystemUI/src/com/android/systemui/recents/IRecentsSystemUserCallbacks.aidl @@ -31,4 +31,5 @@ oneway interface IRecentsSystemUserCallbacks { void sendRecentsDrawnEvent(); void sendDockingTopTaskEvent(int dragMode, in Rect initialRect); void sendLaunchRecentsEvent(); + void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart); } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index de2ace4c30d1a..3800c8db7de7f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -57,6 +57,7 @@ import com.android.systemui.recents.events.activity.DockedTopTaskEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; import com.android.systemui.recents.events.component.RecentsVisibilityChangedEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; +import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; import com.android.systemui.recents.events.component.ShowUserToastEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.misc.SystemServicesProxy; @@ -711,6 +712,25 @@ public class Recents extends SystemUI } } + public final void onBusEvent(SetWaitingForTransitionStartEvent event) { + int processUser = sSystemServicesProxy.getProcessUser(); + if (sSystemServicesProxy.isSystemUser(processUser)) { + mImpl.setWaitingForTransitionStart(event.waitingForTransitionStart); + } else { + postToSystemUser(new Runnable() { + @Override + public void run() { + try { + mUserToSystemCallbacks.setWaitingForTransitionStartEvent( + event.waitingForTransitionStart); + } catch (RemoteException e) { + Log.e(TAG, "Callback failed", e); + } + } + }); + } + } + /** * Attempts to register with the system user. */ diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java index 42e892136954b..cfc49a80f0147 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java @@ -24,12 +24,11 @@ import static android.view.View.MeasureSpec; import android.app.ActivityManager; import android.app.ActivityManager.TaskSnapshot; import android.app.ActivityOptions; +import android.app.ActivityOptions.OnAnimationStartedListener; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.GraphicBuffer; import android.graphics.Rect; import android.graphics.RectF; @@ -208,6 +207,20 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener protected static RecentsTaskLoadPlan sInstanceLoadPlan; // Stores the last pinned task time protected static long sLastPipTime = -1; + // Stores whether we are waiting for a transition to/from recents to start. During this time, + // we disallow the user from manually toggling recents until the transition has started. + private static boolean mWaitingForTransitionStart = false; + // Stores whether or not the user toggled while we were waiting for a transition to/from + // recents. In this case, we defer the toggle state until then and apply it immediately after. + private static boolean mToggleFollowingTransitionStart = true; + + private ActivityOptions.OnAnimationStartedListener mResetToggleFlagListener = + new OnAnimationStartedListener() { + @Override + public void onAnimationStarted() { + setWaitingForTransitionStart(false); + } + }; protected Context mContext; protected Handler mHandler; @@ -365,6 +378,11 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener return; } + if (mWaitingForTransitionStart) { + mToggleFollowingTransitionStart = true; + return; + } + mDraggingInRecents = false; mLaunchedWhileDocking = false; mTriggeredFromAltTab = false; @@ -638,6 +656,18 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } } + public void setWaitingForTransitionStart(boolean waitingForTransitionStart) { + if (mWaitingForTransitionStart == waitingForTransitionStart) { + return; + } + + mWaitingForTransitionStart = waitingForTransitionStart; + if (!waitingForTransitionStart && mToggleFollowingTransitionStart) { + toggleRecents(DividerView.INVALID_RECENTS_GROW_TARGET); + } + mToggleFollowingTransitionStart = false; + } + /** * Returns the preloaded load plan and invalidates it. */ @@ -865,8 +895,9 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } AppTransitionAnimationSpec[] specsArray = new AppTransitionAnimationSpec[specs.size()]; specs.toArray(specsArray); + return new Pair<>(ActivityOptions.makeThumbnailAspectScaleDownAnimation(mDummyStackView, - specsArray, mHandler, null, this), null); + specsArray, mHandler, mResetToggleFlagListener, this), null); } else { // Update the destination rect Task toTask = new Task(); @@ -884,8 +915,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener return Lists.newArrayList(new AppTransitionAnimationSpec( toTask.key.id, thumbnail, rect)); }); + return new Pair<>(ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext, - mHandler, future.getFuture(), null, false /* scaleUp */), future); + mHandler, future.getFuture(), mResetToggleFlagListener, false /* scaleUp */), + future); } } @@ -991,6 +1024,10 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener launchState.launchedToTaskId = runningTaskId; launchState.launchedWithAltTab = mTriggeredFromAltTab; + // Disable toggling of recents between starting the activity and it is visible and the app + // has started its transition into recents. + setWaitingForTransitionStart(useThumbnailTransition); + // Preload the icon (this will be a null-op if we have preloaded the icon already in // preloadRecents()) preloadIcon(runningTaskId); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java index 3921a20cffd17..1285626015d21 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsSystemUser.java @@ -29,6 +29,7 @@ import com.android.systemui.EventLogTags; import com.android.systemui.recents.events.EventBus; import com.android.systemui.recents.events.activity.DockedTopTaskEvent; import com.android.systemui.recents.events.activity.RecentsActivityStartingEvent; +import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; import com.android.systemui.recents.events.ui.RecentsDrawnEvent; import com.android.systemui.recents.misc.ForegroundThread; @@ -105,4 +106,10 @@ public class RecentsSystemUser extends IRecentsSystemUserCallbacks.Stub { public void sendLaunchRecentsEvent() throws RemoteException { EventBus.getDefault().post(new RecentsActivityStartingEvent()); } + + @Override + public void setWaitingForTransitionStartEvent(boolean waitingForTransitionStart) { + EventBus.getDefault().post(new SetWaitingForTransitionStartEvent( + waitingForTransitionStart)); + } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java new file mode 100644 index 0000000000000..d9cf5fbf645d3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/recents/events/component/SetWaitingForTransitionStartEvent.java @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2017 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.systemui.recents.events.component; + +import com.android.systemui.recents.events.EventBus; + +/** + * This is sent when we are setting/resetting the flag to wait for the transition to start. + */ +public class SetWaitingForTransitionStartEvent extends EventBus.Event { + + public final boolean waitingForTransitionStart; + + public SetWaitingForTransitionStartEvent(boolean waitingForTransitionStart) { + this.waitingForTransitionStart = waitingForTransitionStart; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index 968b77f17e626..67685b8737ce7 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -54,6 +54,7 @@ import com.android.systemui.recents.events.activity.LaunchTaskFailedEvent; import com.android.systemui.recents.events.activity.LaunchTaskStartedEvent; import com.android.systemui.recents.events.activity.LaunchTaskSucceededEvent; import com.android.systemui.recents.events.component.ScreenPinningRequestEvent; +import com.android.systemui.recents.events.component.SetWaitingForTransitionStartEvent; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.recents.model.Task; import com.android.systemui.recents.model.TaskStack; @@ -117,31 +118,58 @@ public class RecentsTransitionHelper { final Rect windowRect = Recents.getSystemServices().getWindowRect(); transitionFuture = getAppTransitionFuture( () -> composeAnimationSpecs(task, stackView, destinationStack, windowRect)); - animStartedListener = () -> { - // If we are launching into another task, cancel the previous task's - // window transition - EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); - EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); - stackView.cancelAllTaskViewAnimations(); + animStartedListener = new OnAnimationStartedListener() { + private boolean mHandled; - if (screenPinningRequested) { - // Request screen pinning after the animation runs - mStartScreenPinningRunnable.taskId = task.key.id; - mHandler.postDelayed(mStartScreenPinningRunnable, 350); + @Override + public void onAnimationStarted() { + if (mHandled) { + return; + } + mHandled = true; + + // If we are launching into another task, cancel the previous task's + // window transition + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); + stackView.cancelAllTaskViewAnimations(); + + if (screenPinningRequested) { + // Request screen pinning after the animation runs + mStartScreenPinningRunnable.taskId = task.key.id; + mHandler.postDelayed(mStartScreenPinningRunnable, 350); + } + + // Reset the state where we are waiting for the transition to start + EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false)); } }; } else { // This is only the case if the task is not on screen (scrolled offscreen for example) transitionFuture = null; - animStartedListener = () -> { - // If we are launching into another task, cancel the previous task's - // window transition - EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); - EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); - stackView.cancelAllTaskViewAnimations(); + animStartedListener = new OnAnimationStartedListener() { + private boolean mHandled; + + @Override + public void onAnimationStarted() { + if (mHandled) { + return; + } + mHandled = true; + + // If we are launching into another task, cancel the previous task's + // window transition + EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); + EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); + stackView.cancelAllTaskViewAnimations(); + + // Reset the state where we are waiting for the transition to start + EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(false)); + } }; } + EventBus.getDefault().send(new SetWaitingForTransitionStartEvent(true)); final ActivityOptions opts = ActivityOptions.makeMultiThumbFutureAspectScaleAnimation(mContext, mHandler, transitionFuture != null ? transitionFuture.future : null, animStartedListener, true /* scaleUp */); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 75467f5145407..9b1ddd73dd7f5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -13365,7 +13365,6 @@ public class ActivityManagerService extends IActivityManager.Stub final ActivityRecord r = ActivityRecord.isInStackLocked(token); if (r != null) { final ActivityOptions activityOptions = r.pendingOptions; - r.pendingOptions = null; return activityOptions == null ? null : activityOptions.toBundle(); } return null;