Merge "Implement back to launcher animation from shell and WM animation controllers." into tm-dev

This commit is contained in:
Shan Huang
2022-03-15 05:38:25 +00:00
committed by Android (Google) Code Review
6 changed files with 219 additions and 44 deletions

View File

@@ -1309,6 +1309,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"-711194343": {
"message": "Setting Activity.mLauncherTaskBehind to false. Activity=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"-706481945": {
"message": "TaskFragment parent info changed name=%s parentTaskId=%d",
"level": "VERBOSE",
@@ -2587,6 +2593,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/WindowState.java"
},
"599897753": {
"message": "Previous Activity is %s. Back type is %s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"600140673": {
"message": "checkBootAnimationComplete: Waiting for anim complete",
"level": "INFO",
@@ -2671,12 +2683,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
"664667685": {
"message": "Activity %s: enableOnBackInvokedCallback=false. Returning null BackNavigationInfo.",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"665256544": {
"message": "All windows drawn!",
"level": "DEBUG",
@@ -2887,6 +2893,12 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/TaskFragment.java"
},
"948208142": {
"message": "Setting Activity.mLauncherTaskBehind to true. Activity=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"950074526": {
"message": "setLockTaskMode: Can't lock due to auth",
"level": "WARN",
@@ -3103,6 +3115,12 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/DisplayContent.java"
},
"1172542963": {
"message": "onBackNavigationDone backType=%s, task=%s, prevTaskTopActivity=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
},
"1178653181": {
"message": "Old wallpaper still the target.",
"level": "VERBOSE",
@@ -3415,11 +3433,11 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/ActivityRecord.java"
},
"1554795024": {
"message": "Previous Activity is %s",
"1544805551": {
"message": "Skipping app transition animation. task=%s",
"level": "DEBUG",
"group": "WM_DEBUG_BACK_PREVIEW",
"at": "com\/android\/server\/wm\/BackNavigationController.java"
"at": "com\/android\/server\/wm\/Task.java"
},
"1557732761": {
"message": "For Intent %s bringing to top: %s",

View File

@@ -52,14 +52,17 @@ import com.android.wm.shell.common.annotations.ShellMainThread;
*/
public class BackAnimationController implements RemoteCallable<BackAnimationController> {
private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
public static final boolean IS_ENABLED = SystemProperties
.getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
private static final String BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP =
"persist.debug.back_predictability_progress_threshold";
// By default, enable new back dispatching without any animations.
private static final int BACK_PREDICTABILITY_PROP =
SystemProperties.getInt("persist.debug.back_predictability", 1);
public static final boolean IS_ENABLED = BACK_PREDICTABILITY_PROP > 0;
private static final int PROGRESS_THRESHOLD = SystemProperties
.getInt(BACK_PREDICTABILITY_PROGRESS_THRESHOLD_PROP, -1);
private static final String TAG = "BackAnimationController";
@VisibleForTesting
boolean mEnableAnimations = (BACK_PREDICTABILITY_PROP & (1 << 1)) != 0;
/**
* Location of the initial touch event of the back gesture.
@@ -255,7 +258,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
backNavigationInfo.getTaskWindowConfiguration());
}
mTransaction.apply();
} else if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME) {
} else if (shouldDispatchToLauncher(backType)) {
targetCallback = mBackToLauncherCallback;
} else if (backType == BackNavigationInfo.TYPE_CALLBACK) {
targetCallback = mBackNavigationInfo.getOnBackInvokedCallback();
@@ -309,7 +312,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
BackEvent backEvent = new BackEvent(0, 0, progress, swipeEdge, animationTarget);
IOnBackInvokedCallback targetCallback = null;
if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME) {
if (shouldDispatchToLauncher(backType)) {
targetCallback = mBackToLauncherCallback;
} else if (backType == BackNavigationInfo.TYPE_CROSS_TASK
|| backType == BackNavigationInfo.TYPE_CROSS_ACTIVITY) {
@@ -330,8 +333,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
int backType = mBackNavigationInfo.getType();
boolean shouldDispatchToLauncher = backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
&& mBackToLauncherCallback != null;
boolean shouldDispatchToLauncher = shouldDispatchToLauncher(backType);
IOnBackInvokedCallback targetCallback = shouldDispatchToLauncher
? mBackToLauncherCallback
: mBackNavigationInfo.getOnBackInvokedCallback();
@@ -356,6 +358,17 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
}
}
private boolean shouldDispatchToLauncher(int backType) {
return backType == BackNavigationInfo.TYPE_RETURN_TO_HOME
&& mBackToLauncherCallback != null
&& mEnableAnimations;
}
@VisibleForTesting
void setEnableAnimations(boolean shouldEnable) {
mEnableAnimations = shouldEnable;
}
private static void dispatchOnBackStarted(IOnBackInvokedCallback callback) {
if (callback == null) {
return;
@@ -468,7 +481,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont
return;
}
RemoteAnimationTarget animationTarget = backNavigationInfo.getDepartingAnimationTarget();
if (animationTarget != null && mTriggerBack) {
if (animationTarget != null) {
if (animationTarget.leash != null && animationTarget.leash.isValid()) {
mTransaction.remove(animationTarget.leash);
}

View File

@@ -78,6 +78,7 @@ public class BackAnimationControllerTest {
MockitoAnnotations.initMocks(this);
mController = new BackAnimationController(
mShellExecutor, mTransaction, mActivityTaskManager, mContext);
mController.setEnableAnimations(true);
}
private void createNavigationInfo(RemoteAnimationTarget topAnimationTarget,

View File

@@ -25,6 +25,7 @@ import android.content.ComponentName;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.HardwareBuffer;
import android.os.Bundle;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -44,7 +45,11 @@ import com.android.internal.protolog.common.ProtoLog;
class BackNavigationController {
private static final String TAG = "BackNavigationController";
private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
// By default, enable new back dispatching without any animations.
private static final int BACK_PREDICTABILITY_PROP =
SystemProperties.getInt("persist.debug.back_predictability", 1);
private static final int ANIMATIONS_MASK = 1 << 1;
private static final int SCREENSHOT_MASK = 1 << 2;
@Nullable
private TaskSnapshotController mTaskSnapshotController;
@@ -53,11 +58,15 @@ class BackNavigationController {
* Returns true if the back predictability feature is enabled
*/
static boolean isEnabled() {
return SystemProperties.getInt(BACK_PREDICTABILITY_PROP, 1) > 0;
return BACK_PREDICTABILITY_PROP > 0;
}
static boolean isScreenshotEnabled() {
return false;
return (BACK_PREDICTABILITY_PROP & SCREENSHOT_MASK) != 0;
}
private static boolean isAnimationEnabled() {
return (BACK_PREDICTABILITY_PROP & ANIMATIONS_MASK) != 0;
}
/**
@@ -93,14 +102,17 @@ class BackNavigationController {
ActivityRecord prev;
WindowContainer<?> removedWindowContainer;
ActivityRecord activityRecord;
ActivityRecord prevTaskTopActivity = null;
SurfaceControl animationLeashParent;
WindowConfiguration taskWindowConfiguration;
HardwareBuffer screenshotBuffer = null;
SurfaceControl screenshotSurface;
int prevTaskId;
int prevUserId;
RemoteAnimationTarget topAppTarget;
SurfaceControl animLeash;
IOnBackInvokedCallback callback = null;
IOnBackInvokedCallback applicationCallback = null;
IOnBackInvokedCallback systemCallback = null;
synchronized (task.mWmService.mGlobalLock) {
@@ -116,15 +128,14 @@ class BackNavigationController {
removedWindowContainer = activityRecord;
taskWindowConfiguration = window.getWindowConfiguration();
}
IOnBackInvokedCallback applicationCallback = null;
IOnBackInvokedCallback systemCallback = null;
if (window != null) {
applicationCallback = window.getApplicationOnBackInvokedCallback();
callback = applicationCallback;
if (callback == null) {
systemCallback = window.getSystemOnBackInvokedCallback();
callback = systemCallback;
}
systemCallback = window.getSystemOnBackInvokedCallback();
}
if (applicationCallback == null && systemCallback == null) {
// Return null when either there's no window, or apps have just initialized and
// have not finished registering callbacks.
return null;
}
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, "
@@ -133,24 +144,24 @@ class BackNavigationController {
task, activityRecord, applicationCallback, systemCallback);
// TODO Temp workaround for Sysui until b/221071505 is fixed
if (activityRecord == null && callback != null) {
if (activityRecord == null && applicationCallback != null) {
return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK,
null /* topWindowLeash */, null /* screenshotSurface */,
null /* screenshotBuffer */, null /* taskWindowConfiguration */,
null /* onBackNavigationDone */,
callback /* onBackInvokedCallback */);
applicationCallback /* onBackInvokedCallback */);
}
// For IME and Home, either a callback is registered, or we do nothing. In both cases,
// we don't need to pass the leashes below.
if (activityRecord == null || task.getDisplayContent().getImeContainer().isVisible()
|| activityRecord.isActivityTypeHome()) {
if (callback != null) {
if (applicationCallback != null) {
return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK,
null /* topWindowLeash */, null /* screenshotSurface */,
null /* screenshotBuffer */, null /* taskWindowConfiguration */,
null /* onBackNavigationDone */,
callback /* onBackInvokedCallback */);
applicationCallback /* onBackInvokedCallback */);
} else {
return null;
}
@@ -159,12 +170,12 @@ class BackNavigationController {
prev = task.getActivity(
(r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity());
if (callback != null) {
if (applicationCallback != null) {
return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK,
null /* topWindowLeash */, null /* screenshotSurface */,
null /* screenshotBuffer */, null /* taskWindowConfiguration */,
null /* onBackNavigationDone */,
callback /* onBackInvokedCallback */);
applicationCallback /* onBackInvokedCallback */);
} else if (prev != null) {
backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
} else if (task.returnsToHomeRootTask()) {
@@ -188,8 +199,8 @@ class BackNavigationController {
prevTaskId = prevTask != null ? prevTask.mTaskId : 0;
prevUserId = prevTask != null ? prevTask.mUserId : 0;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s",
prev != null ? prev.mActivityComponent : null);
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Previous Activity is %s. "
+ "Back type is %s", prev != null ? prev.mActivityComponent : null, backType);
//TODO(207481538) Remove once the infrastructure to support per-activity screenshot is
// implemented. For now we simply have the mBackScreenshots hash map that dumbly
@@ -204,6 +215,7 @@ class BackNavigationController {
return null;
}
// Prepare a leash to animate the current top window
// TODO(b/220934562): Use surface animator to better manage animation conflicts.
animLeash = removedWindowContainer.makeAnimationLeash()
.setName("BackPreview Leash for " + removedWindowContainer)
.setHidden(false)
@@ -231,12 +243,30 @@ class BackNavigationController {
activityRecord.windowType);
}
SurfaceControl.Builder builder = new SurfaceControl.Builder()
screenshotSurface = new SurfaceControl.Builder()
.setName("BackPreview Screenshot for " + prev)
.setParent(animationLeashParent)
.setHidden(false)
.setBLASTLayer();
SurfaceControl screenshotSurface = builder.build();
.setBLASTLayer()
.build();
if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) {
task.mBackGestureStarted = true;
// Make launcher show from behind by marking its top activity as visible and
// launch-behind to bump its visibility for the duration of the back gesture.
prevTaskTopActivity = prevTask.getTopNonFinishingActivity();
if (prevTaskTopActivity != null) {
if (!prevTaskTopActivity.mVisibleRequested) {
prevTaskTopActivity.setVisibility(true);
}
prevTaskTopActivity.mLaunchTaskBehind = true;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to true. Activity=%s",
prevTaskTopActivity);
prevTaskTopActivity.mRootWindowContainer.ensureActivitiesVisible(
null /* starting */, 0 /* configChanges */,
false /* preserveWindows */);
}
}
// Find a screenshot of the previous activity
@@ -253,17 +283,20 @@ class BackNavigationController {
WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
try {
activityRecord.token.linkToDeath(
() -> resetSurfaces(finalRemovedWindowContainer), 0);
activityRecord.token.linkToDeath(() -> resetSurfaces(finalRemovedWindowContainer), 0);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to link to death", e);
resetSurfaces(removedWindowContainer);
return null;
}
RemoteCallback onBackNavigationDone = new RemoteCallback(
result -> resetSurfaces(finalRemovedWindowContainer
));
int finalBackType = backType;
final IOnBackInvokedCallback callback =
applicationCallback != null ? applicationCallback : systemCallback;
ActivityRecord finalPrevTaskTopActivity = prevTaskTopActivity;
RemoteCallback onBackNavigationDone = new RemoteCallback(result -> onBackNavigationDone(
result, finalRemovedWindowContainer, finalBackType, task,
finalPrevTaskTopActivity));
return new BackNavigationInfo(backType,
topAppTarget,
screenshotSurface,
@@ -273,6 +306,39 @@ class BackNavigationController {
callback);
}
private void onBackNavigationDone(
Bundle result, WindowContainer windowContainer, int backType,
Task task, ActivityRecord prevTaskTopActivity) {
SurfaceControl surfaceControl = windowContainer.getSurfaceControl();
boolean triggerBack = result != null
? result.getBoolean(BackNavigationInfo.KEY_TRIGGER_BACK)
: false;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "onBackNavigationDone backType=%s, "
+ "task=%s, prevTaskTopActivity=%s", backType, task, prevTaskTopActivity);
if (backType == BackNavigationInfo.TYPE_RETURN_TO_HOME && isAnimationEnabled()) {
if (triggerBack) {
if (surfaceControl != null && surfaceControl.isValid()) {
// When going back to home, hide the task surface before it is re-parented to
// avoid flicker.
SurfaceControl.Transaction t = windowContainer.getSyncTransaction();
t.hide(surfaceControl);
t.apply();
}
}
if (prevTaskTopActivity != null && !triggerBack) {
// Restore the launch-behind state.
task.mTaskSupervisor.scheduleLaunchTaskBehindComplete(prevTaskTopActivity.token);
prevTaskTopActivity.mLaunchTaskBehind = false;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW,
"Setting Activity.mLauncherTaskBehind to false. Activity=%s",
prevTaskTopActivity);
}
} else {
task.mBackGestureStarted = false;
}
resetSurfaces(windowContainer);
}
private HardwareBuffer getActivitySnapshot(@NonNull Task task,
ComponentName activityComponent) {

View File

@@ -72,6 +72,7 @@ import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.policy.DecorView.DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_LOCKTASK;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_RECENTS_ANIMATIONS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
@@ -609,6 +610,12 @@ class Task extends TaskFragment {
boolean mLastSurfaceShowing = true;
/**
* Tracks if a back gesture is in progress.
* Skips any system transition animations if this is set to {@code true}.
*/
boolean mBackGestureStarted = false;
private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
Intent _affinityIntent, String _affinity, String _rootAffinity,
ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
@@ -3322,6 +3329,14 @@ class Task extends TaskFragment {
}
});
}
} else if (mBackGestureStarted) {
// Cancel playing transitions if a back navigation animation is in progress.
// This bit is set by {@link BackNavigationController} when a back gesture is started.
// It is used as a one-off transition overwrite that is cleared when the back gesture
// is committed and triggers a transition, or when the gesture is cancelled.
mBackGestureStarted = false;
mDisplayContent.mSkipAppTransitionAnimation = true;
ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "Skipping app transition animation. task=%s", this);
} else {
super.applyAnimationUnchecked(lp, enter, transit, isVoiceInteraction, sources);
}

View File

@@ -30,7 +30,10 @@ import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.hardware.HardwareBuffer;
import android.platform.test.annotations.Presubmit;
import android.window.BackEvent;
import android.window.BackNavigationInfo;
import android.window.IOnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
import android.window.TaskSnapshot;
import org.junit.Before;
@@ -42,15 +45,19 @@ import org.junit.runner.RunWith;
public class BackNavigationControllerTests extends WindowTestsBase {
private BackNavigationController mBackNavigationController;
private IOnBackInvokedCallback mOnBackInvokedCallback;
@Before
public void setUp() throws Exception {
mBackNavigationController = new BackNavigationController();
mOnBackInvokedCallback = createBackCallback();
}
@Test
public void backTypeHomeWhenBackToLauncher() {
Task task = createTopTaskWithActivity();
registerSystemOnBackInvokedCallback();
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
@@ -63,6 +70,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
Task taskA = createTask(mDefaultDisplay);
createActivityRecord(taskA);
Task task = createTopTaskWithActivity();
registerSystemOnBackInvokedCallback();
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
@@ -75,6 +84,8 @@ public class BackNavigationControllerTests extends WindowTestsBase {
Task task = createTopTaskWithActivity();
mAtm.setFocusedTask(task.mTaskId,
createAppWindow(task, FIRST_APPLICATION_WINDOW, "window").mActivityRecord);
registerSystemOnBackInvokedCallback();
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
@@ -89,6 +100,7 @@ public class BackNavigationControllerTests extends WindowTestsBase {
public void backNavInfoFullyPopulated() {
Task task = createTopTaskWithActivity();
createAppWindow(task, FIRST_APPLICATION_WINDOW, "window");
registerSystemOnBackInvokedCallback();
// We need a mock screenshot so
TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController();
@@ -104,6 +116,30 @@ public class BackNavigationControllerTests extends WindowTestsBase {
assertThat(backNavigationInfo.getTaskWindowConfiguration()).isNotNull();
}
@Test
public void preparesForBackToHome() {
Task task = createTopTaskWithActivity();
ActivityRecord activity = task.getTopActivity(false, false);
registerSystemOnBackInvokedCallback();
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_RETURN_TO_HOME));
}
@Test
public void backTypeCallback() {
Task task = createTopTaskWithActivity();
ActivityRecord activity = task.getTopActivity(false, false);
registerApplicationOnBackInvokedCallback();
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(typeToString(backNavigationInfo.getType()))
.isEqualTo(typeToString(BackNavigationInfo.TYPE_CALLBACK));
}
@NonNull
private TaskSnapshotController createMockTaskSnapshotController() {
TaskSnapshotController taskSnapshotController = mock(TaskSnapshotController.class);
@@ -126,4 +162,30 @@ public class BackNavigationControllerTests extends WindowTestsBase {
mAtm.setFocusedTask(task.mTaskId, record);
return task;
}
private void registerSystemOnBackInvokedCallback() {
mWm.getFocusedWindowLocked().setOnBackInvokedCallback(
mOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
}
private void registerApplicationOnBackInvokedCallback() {
mWm.getFocusedWindowLocked().setOnBackInvokedCallback(
mOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
}
private IOnBackInvokedCallback createBackCallback() {
return new IOnBackInvokedCallback.Stub() {
@Override
public void onBackStarted() { }
@Override
public void onBackProgressed(BackEvent backEvent) { }
@Override
public void onBackCancelled() { }
@Override
public void onBackInvoked() { }
};
}
}