Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 850e4d6e authored by Wei Sheng Shih's avatar Wei Sheng Shih Committed by Automerger Merge Worker
Browse files

Merge "Provide config for customize the showing strategy of back target." into...

Merge "Provide config for customize the showing strategy of back target." into udc-dev am: b11b27d4

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21578380



Change-Id: Ie595887f9ea5da12ff6916fcf179451e11f1a618
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents e18666b1 b11b27d4
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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>

+1 −0
Original line number Diff line number Diff line
@@ -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" />
+43 −34
Original line number Diff line number Diff line
@@ -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) {
+77 −0
Original line number Diff line number Diff line
@@ -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);