Merge "Provide config for customize the showing strategy of back target." into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
b11b27d490
@@ -3345,6 +3345,11 @@
|
||||
<!-- The duration in which a recent task is considered in session and should be visible. -->
|
||||
<integer name="config_activeTaskDurationHours">6</integer>
|
||||
|
||||
<!-- Whether this device prefers to show snapshot or splash screen on back predict target.
|
||||
When set true, there will create windowless starting surface for the preview target, so it
|
||||
won't affect activity's lifecycle. This should only be disabled on low-ram device. -->
|
||||
<bool name="config_predictShowStartingSurface">true</bool>
|
||||
|
||||
<!-- default window ShowCircularMask property -->
|
||||
<bool name="config_windowShowCircularMask">false</bool>
|
||||
|
||||
|
||||
@@ -393,6 +393,7 @@
|
||||
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks" />
|
||||
<java-symbol type="integer" name="config_activeTaskDurationHours" />
|
||||
<java-symbol type="bool" name="config_windowShowCircularMask" />
|
||||
<java-symbol type="bool" name="config_predictShowStartingSurface" />
|
||||
<java-symbol type="bool" name="config_windowEnableCircularEmulatorDisplayOverlay" />
|
||||
<java-symbol type="bool" name="config_supportMicNearUltrasound" />
|
||||
<java-symbol type="bool" name="config_supportSpeakerNearUltrasound" />
|
||||
|
||||
@@ -32,6 +32,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_PREDICT_BACK;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.content.res.ResourceId;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
@@ -75,7 +76,7 @@ class BackNavigationController {
|
||||
private Runnable mPendingAnimation;
|
||||
private final NavigationMonitor mNavigationMonitor = new NavigationMonitor();
|
||||
|
||||
private AnimationHandler mAnimationHandler;
|
||||
AnimationHandler mAnimationHandler;
|
||||
private final ArrayList<WindowContainer> mTmpOpenApps = new ArrayList<>();
|
||||
private final ArrayList<WindowContainer> mTmpCloseApps = new ArrayList<>();
|
||||
|
||||
@@ -651,7 +652,8 @@ class BackNavigationController {
|
||||
/**
|
||||
* Create and handling animations status for an open/close animation targets.
|
||||
*/
|
||||
private static class AnimationHandler {
|
||||
static class AnimationHandler {
|
||||
private final boolean mShowWindowlessSurface;
|
||||
private final WindowManagerService mWindowManagerService;
|
||||
private BackWindowAnimationAdaptor mCloseAdaptor;
|
||||
private BackWindowAnimationAdaptor mOpenAdaptor;
|
||||
@@ -670,6 +672,9 @@ class BackNavigationController {
|
||||
|
||||
AnimationHandler(WindowManagerService wms) {
|
||||
mWindowManagerService = wms;
|
||||
final Context context = wms.mContext;
|
||||
mShowWindowlessSurface = context.getResources().getBoolean(
|
||||
com.android.internal.R.bool.config_predictShowStartingSurface);
|
||||
}
|
||||
private static final int UNKNOWN = 0;
|
||||
private static final int TASK_SWITCH = 1;
|
||||
@@ -714,7 +719,8 @@ class BackNavigationController {
|
||||
}
|
||||
}
|
||||
|
||||
boolean composeAnimations(@NonNull WindowContainer close, @NonNull WindowContainer open) {
|
||||
private boolean composeAnimations(@NonNull WindowContainer close,
|
||||
@NonNull WindowContainer open) {
|
||||
clearBackAnimateTarget(null /* cleanupTransaction */);
|
||||
if (close == null || open == null) {
|
||||
Slog.e(TAG, "reset animation with null target close: "
|
||||
@@ -998,21 +1004,20 @@ class BackNavigationController {
|
||||
case BackNavigationInfo.TYPE_CROSS_ACTIVITY:
|
||||
return new ScheduleAnimationBuilder(backType, adapter)
|
||||
.setComposeTarget(currentActivity, previousActivity)
|
||||
.setOpeningSnapshot(getActivitySnapshot(previousActivity));
|
||||
.setIsLaunchBehind(false);
|
||||
case BackNavigationInfo.TYPE_CROSS_TASK:
|
||||
return new ScheduleAnimationBuilder(backType, adapter)
|
||||
.setComposeTarget(currentTask, previousTask)
|
||||
.setOpeningSnapshot(getTaskSnapshot(previousTask));
|
||||
.setIsLaunchBehind(false);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private class ScheduleAnimationBuilder {
|
||||
class ScheduleAnimationBuilder {
|
||||
final int mType;
|
||||
final BackAnimationAdapter mBackAnimationAdapter;
|
||||
WindowContainer mCloseTarget;
|
||||
WindowContainer mOpenTarget;
|
||||
TaskSnapshot mOpenSnapshot;
|
||||
boolean mIsLaunchBehind;
|
||||
|
||||
ScheduleAnimationBuilder(int type, BackAnimationAdapter backAnimationAdapter) {
|
||||
@@ -1026,11 +1031,6 @@ class BackNavigationController {
|
||||
return this;
|
||||
}
|
||||
|
||||
ScheduleAnimationBuilder setOpeningSnapshot(TaskSnapshot snapshot) {
|
||||
mOpenSnapshot = snapshot;
|
||||
return this;
|
||||
}
|
||||
|
||||
ScheduleAnimationBuilder setIsLaunchBehind(boolean launchBehind) {
|
||||
mIsLaunchBehind = launchBehind;
|
||||
return this;
|
||||
@@ -1041,17 +1041,32 @@ class BackNavigationController {
|
||||
|| wc.hasChild(mOpenTarget) || wc.hasChild(mCloseTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply preview strategy on the opening target
|
||||
* @param open The opening target.
|
||||
* @param visibleOpenActivity The visible activity in opening target.
|
||||
* @return If the preview strategy is launch behind, returns the Activity that has
|
||||
* launchBehind set, or null otherwise.
|
||||
*/
|
||||
private ActivityRecord applyPreviewStrategy(WindowContainer open,
|
||||
ActivityRecord visibleOpenActivity) {
|
||||
if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
|
||||
createStartingSurface(getSnapshot(open));
|
||||
return null;
|
||||
}
|
||||
setLaunchBehind(visibleOpenActivity);
|
||||
return visibleOpenActivity;
|
||||
}
|
||||
|
||||
Runnable build() {
|
||||
if (mOpenTarget == null || mCloseTarget == null) {
|
||||
return null;
|
||||
}
|
||||
final boolean shouldLaunchBehind = mIsLaunchBehind || !isSupportWindowlessSurface();
|
||||
final ActivityRecord launchBehindActivity = !shouldLaunchBehind ? null
|
||||
: mOpenTarget.asTask() != null
|
||||
final ActivityRecord openActivity = mOpenTarget.asTask() != null
|
||||
? mOpenTarget.asTask().getTopNonFinishingActivity()
|
||||
: mOpenTarget.asActivityRecord() != null
|
||||
? mOpenTarget.asActivityRecord() : null;
|
||||
if (shouldLaunchBehind && launchBehindActivity == null) {
|
||||
if (openActivity == null) {
|
||||
Slog.e(TAG, "No opening activity");
|
||||
return null;
|
||||
}
|
||||
@@ -1059,11 +1074,8 @@ class BackNavigationController {
|
||||
if (!composeAnimations(mCloseTarget, mOpenTarget)) {
|
||||
return null;
|
||||
}
|
||||
if (launchBehindActivity != null) {
|
||||
setLaunchBehind(launchBehindActivity);
|
||||
} else {
|
||||
createStartingSurface(mOpenSnapshot);
|
||||
}
|
||||
final ActivityRecord launchBehindActivity =
|
||||
applyPreviewStrategy(mOpenTarget, openActivity);
|
||||
|
||||
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback(
|
||||
launchBehindActivity != null ? triggerBack -> {
|
||||
@@ -1186,25 +1198,22 @@ class BackNavigationController {
|
||||
mPendingAnimationBuilder = null;
|
||||
}
|
||||
|
||||
private static TaskSnapshot getActivitySnapshot(@NonNull ActivityRecord r) {
|
||||
static TaskSnapshot getSnapshot(@NonNull WindowContainer w) {
|
||||
if (!isScreenshotEnabled()) {
|
||||
return null;
|
||||
}
|
||||
// Check if we have a screenshot of the previous activity, indexed by its
|
||||
// component name.
|
||||
// TODO return TaskSnapshot when feature complete.
|
||||
// final HardwareBuffer hw = r.getTask().getSnapshotForActivityRecord(r);
|
||||
return null;
|
||||
}
|
||||
if (w.asTask() != null) {
|
||||
final Task task = w.asTask();
|
||||
return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
|
||||
task.mTaskId, task.mUserId, false /* restoreFromDisk */,
|
||||
false /* isLowResolution */);
|
||||
}
|
||||
|
||||
private static TaskSnapshot getTaskSnapshot(Task task) {
|
||||
if (!isScreenshotEnabled()) {
|
||||
if (w.asActivityRecord() != null) {
|
||||
// TODO (b/259497289) return TaskSnapshot when feature complete.
|
||||
return null;
|
||||
}
|
||||
// Don't read from disk!!
|
||||
return task.mRootWindowContainer.mWindowManager.mTaskSnapshotController.getSnapshot(
|
||||
task.mTaskId, task.mUserId, false /* restoreFromDisk */,
|
||||
false /* isLowResolution */);
|
||||
return null;
|
||||
}
|
||||
|
||||
void setWindowManager(WindowManagerService wm) {
|
||||
|
||||
@@ -26,25 +26,31 @@ import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
|
||||
import static android.window.BackNavigationInfo.typeToString;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static com.google.common.truth.Truth.assertWithMessage;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
@@ -58,6 +64,7 @@ import android.window.IOnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedCallback;
|
||||
import android.window.OnBackInvokedCallbackInfo;
|
||||
import android.window.OnBackInvokedDispatcher;
|
||||
import android.window.TaskSnapshot;
|
||||
import android.window.WindowOnBackInvokedDispatcher;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
@@ -66,6 +73,8 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.MockitoSession;
|
||||
import org.mockito.quality.Strictness;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -408,6 +417,25 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
0, navigationObserver.getCount());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test with
|
||||
* config_predictShowStartingSurface = true
|
||||
*/
|
||||
@Test
|
||||
public void testEnableWindowlessSurface() {
|
||||
testPrepareAnimation(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test with
|
||||
* config_predictShowStartingSurface = false
|
||||
*/
|
||||
@Test
|
||||
public void testDisableWindowlessSurface() {
|
||||
testPrepareAnimation(false);
|
||||
}
|
||||
|
||||
private IOnBackInvokedCallback withSystemCallback(Task task) {
|
||||
IOnBackInvokedCallback callback = createOnBackInvokedCallback();
|
||||
task.getTopMostActivity().getTopChild().setOnBackInvokedCallbackInfo(
|
||||
@@ -492,6 +520,55 @@ public class BackNavigationControllerTests extends WindowTestsBase {
|
||||
doReturn(true).when(kc).isDisplayOccluded(anyInt());
|
||||
}
|
||||
|
||||
private void testPrepareAnimation(boolean preferWindowlessSurface) {
|
||||
final TaskSnapshot taskSnapshot = mock(TaskSnapshot.class);
|
||||
final ContextWrapper contextSpy = Mockito.spy(new ContextWrapper(mWm.mContext));
|
||||
final Resources resourcesSpy = Mockito.spy(contextSpy.getResources());
|
||||
|
||||
when(contextSpy.getResources()).thenReturn(resourcesSpy);
|
||||
|
||||
MockitoSession mockitoSession = mockitoSession().mockStatic(BackNavigationController.class)
|
||||
.strictness(Strictness.LENIENT).startMocking();
|
||||
doReturn(taskSnapshot).when(() -> BackNavigationController.getSnapshot(any()));
|
||||
when(resourcesSpy.getBoolean(
|
||||
com.android.internal.R.bool.config_predictShowStartingSurface))
|
||||
.thenReturn(preferWindowlessSurface);
|
||||
|
||||
final BackNavigationController.AnimationHandler animationHandler =
|
||||
Mockito.spy(new BackNavigationController.AnimationHandler(mWm));
|
||||
doReturn(true).when(animationHandler).isSupportWindowlessSurface();
|
||||
testWithConfig(animationHandler, preferWindowlessSurface);
|
||||
mockitoSession.finishMocking();
|
||||
}
|
||||
|
||||
private void testWithConfig(BackNavigationController.AnimationHandler animationHandler,
|
||||
boolean preferWindowlessSurface) {
|
||||
final Task task = createTask(mDefaultDisplay);
|
||||
final ActivityRecord bottomActivity = createActivityRecord(task);
|
||||
final ActivityRecord homeActivity = mRootHomeTask.getTopNonFinishingActivity();
|
||||
|
||||
final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toHomeBuilder =
|
||||
animationHandler.prepareAnimation(BackNavigationInfo.TYPE_RETURN_TO_HOME,
|
||||
mBackAnimationAdapter, task, mRootHomeTask, bottomActivity, homeActivity);
|
||||
assertTrue(toHomeBuilder.mIsLaunchBehind);
|
||||
toHomeBuilder.build();
|
||||
verify(animationHandler, never()).createStartingSurface(any());
|
||||
|
||||
// Back to ACTIVITY and TASK have the same logic, just with different target.
|
||||
final ActivityRecord topActivity = createActivityRecord(task);
|
||||
final BackNavigationController.AnimationHandler.ScheduleAnimationBuilder toActivityBuilder =
|
||||
animationHandler.prepareAnimation(
|
||||
BackNavigationInfo.TYPE_CROSS_ACTIVITY, mBackAnimationAdapter, task, task,
|
||||
topActivity, bottomActivity);
|
||||
assertFalse(toActivityBuilder.mIsLaunchBehind);
|
||||
toActivityBuilder.build();
|
||||
if (preferWindowlessSurface) {
|
||||
verify(animationHandler).createStartingSurface(any());
|
||||
} else {
|
||||
verify(animationHandler, never()).createStartingSurface(any());
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Task createTopTaskWithActivity() {
|
||||
Task task = createTask(mDefaultDisplay);
|
||||
|
||||
Reference in New Issue
Block a user