diff options
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java (renamed from libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java) | 14 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java | 7 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java | 12 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java | 1 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java | 53 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java | 156 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java | 3 | ||||
| -rw-r--r-- | libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java | 11 |
8 files changed, 192 insertions, 65 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java index 1b1ebc39b558..4cbb78f2dae2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDoubleTapHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDoubleTapHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2024 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. @@ -14,14 +14,12 @@ * limitations under the License. */ -package com.android.wm.shell.pip.phone; +package com.android.wm.shell.common.pip; import android.annotation.IntDef; import android.annotation.NonNull; import android.graphics.Rect; -import com.android.wm.shell.common.pip.PipBoundsState; - import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -50,9 +48,9 @@ public class PipDoubleTapHelper { @Retention(RetentionPolicy.SOURCE) @interface PipSizeSpec {} - static final int SIZE_SPEC_DEFAULT = 0; - static final int SIZE_SPEC_MAX = 1; - static final int SIZE_SPEC_CUSTOM = 2; + public static final int SIZE_SPEC_DEFAULT = 0; + public static final int SIZE_SPEC_MAX = 1; + public static final int SIZE_SPEC_CUSTOM = 2; /** * Returns MAX or DEFAULT {@link PipSizeSpec} to toggle to/from. @@ -84,7 +82,7 @@ public class PipDoubleTapHelper { * @return pip screen size to switch to */ @PipSizeSpec - static int nextSizeSpec(@NonNull PipBoundsState mPipBoundsState, + public static int nextSizeSpec(@NonNull PipBoundsState mPipBoundsState, @NonNull Rect userResizeBounds) { // is pip screen at its maximum boolean isScreenMax = mPipBoundsState.getBounds().width() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java index 3b48c67a5bbd..7b98fa6523cb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java @@ -50,15 +50,16 @@ import java.util.Optional; public abstract class Pip2Module { @WMSingleton @Provides - static PipTransition providePipTransition(@NonNull ShellInit shellInit, + static PipTransition providePipTransition(Context context, + @NonNull ShellInit shellInit, @NonNull ShellTaskOrganizer shellTaskOrganizer, @NonNull Transitions transitions, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, Optional<PipController> pipController, @NonNull PipScheduler pipScheduler) { - return new PipTransition(shellInit, shellTaskOrganizer, transitions, pipBoundsState, null, - pipBoundsAlgorithm, pipScheduler); + return new PipTransition(context, shellInit, shellTaskOrganizer, transitions, + pipBoundsState, null, pipBoundsAlgorithm, pipScheduler); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java index 04911c0bc064..0e7073688ec4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java @@ -47,6 +47,7 @@ import com.android.wm.shell.transition.Transitions; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; /** * Responsible supplying PiP Transitions. @@ -116,6 +117,17 @@ public abstract class PipTransitionController implements Transitions.TransitionH } /** + * Called when the Shell wants to start resizing Pip transition/animation. + * + * @param onFinishResizeCallback callback guaranteed to execute when animation ends and + * client completes any potential draws upon WM state updates. + */ + public void startResizeTransition(WindowContainerTransaction wct, + Consumer<Rect> onFinishResizeCallback) { + // Default implementation does nothing. + } + + /** * Called when the transition animation can't continue (eg. task is removed during * animation) */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 452a41696fcf..81705e20a1df 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -52,6 +52,7 @@ import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipDoubleTapHelper; import com.android.wm.shell.common.pip.PipUiEventLogger; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.common.pip.SizeSpecSource; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java index 0b8f60e44c7e..57b73b3019f4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java @@ -24,10 +24,12 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.graphics.Rect; import android.view.SurfaceControl; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; +import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; @@ -36,6 +38,10 @@ import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.pip.PipTransitionController; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.function.Consumer; + /** * Scheduler for Shell initiated PiP transitions and animations. */ @@ -58,13 +64,37 @@ public class PipScheduler { private SurfaceControl mPinnedTaskLeash; /** - * A temporary broadcast receiver to initiate exit PiP via expand. - * This will later be modified to be triggered by the PiP menu. + * Temporary PiP CUJ codes to schedule PiP related transitions directly from Shell. + * This is used for a broadcast receiver to resolve intents. This should be removed once + * there is an equivalent of PipTouchHandler and PipResizeGestureHandler for PiP2. + */ + private static final int PIP_EXIT_VIA_EXPAND_CODE = 0; + private static final int PIP_DOUBLE_TAP = 1; + + @IntDef(value = { + PIP_EXIT_VIA_EXPAND_CODE, + PIP_DOUBLE_TAP + }) + @Retention(RetentionPolicy.SOURCE) + @interface PipUserJourneyCode {} + + /** + * A temporary broadcast receiver to initiate PiP CUJs. */ private class PipSchedulerReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - scheduleExitPipViaExpand(); + int userJourneyCode = intent.getIntExtra("cuj_code_extra", 0); + switch (userJourneyCode) { + case PIP_EXIT_VIA_EXPAND_CODE: + scheduleExitPipViaExpand(); + break; + case PIP_DOUBLE_TAP: + scheduleDoubleTapToResize(); + break; + default: + throw new IllegalStateException("unexpected CUJ code=" + userJourneyCode); + } } } @@ -121,6 +151,23 @@ public class PipScheduler { } } + /** + * Schedules resize PiP via double tap. + */ + public void scheduleDoubleTapToResize() {} + + /** + * Animates resizing of the pinned stack given the duration. + */ + public void scheduleAnimateResizePip(Rect toBounds, Consumer<Rect> onFinishResizeCallback) { + if (mPipTaskToken == null) { + return; + } + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.setBounds(mPipTaskToken, toBounds); + mPipTransitionController.startResizeTransition(wct, onFinishResizeCallback); + } + void onExitPip() { mPipTaskToken = null; mPinnedTaskLeash = null; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java index 3b0e7c139bed..f3d178aef4ea 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTransition.java @@ -22,10 +22,12 @@ import static android.view.WindowManager.TRANSIT_PIP; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static com.android.wm.shell.transition.Transitions.TRANSIT_EXIT_PIP; +import static com.android.wm.shell.transition.Transitions.TRANSIT_RESIZE_PIP; import android.annotation.NonNull; import android.app.ActivityManager; import android.app.PictureInPictureParams; +import android.content.Context; import android.graphics.Rect; import android.os.IBinder; import android.view.SurfaceControl; @@ -36,6 +38,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.Nullable; +import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; @@ -45,25 +48,29 @@ import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; +import java.util.function.Consumer; + /** * Implementation of transitions for PiP on phone. */ public class PipTransition extends PipTransitionController { private static final String TAG = PipTransition.class.getSimpleName(); + private final Context mContext; private final PipScheduler mPipScheduler; @Nullable private WindowContainerToken mPipTaskToken; @Nullable private IBinder mEnterTransition; @Nullable - private IBinder mAutoEnterButtonNavTransition; - @Nullable private IBinder mExitViaExpandTransition; @Nullable - private IBinder mLegacyEnterTransition; + private IBinder mResizeTransition; + + private Consumer<Rect> mFinishResizeCallback; public PipTransition( + Context context, @NonNull ShellInit shellInit, @NonNull ShellTaskOrganizer shellTaskOrganizer, @NonNull Transitions transitions, @@ -74,6 +81,7 @@ public class PipTransition extends PipTransitionController { super(shellInit, shellTaskOrganizer, transitions, pipBoundsState, pipMenuController, pipBoundsAlgorithm); + mContext = context; mPipScheduler = pipScheduler; mPipScheduler.setPipTransitionController(this); } @@ -87,7 +95,7 @@ public class PipTransition extends PipTransitionController { @Override public void startExitTransition(int type, WindowContainerTransaction out, - @android.annotation.Nullable Rect destinationBounds) { + @Nullable Rect destinationBounds) { if (out == null) { return; } @@ -97,6 +105,16 @@ public class PipTransition extends PipTransitionController { } } + @Override + public void startResizeTransition(WindowContainerTransaction wct, + Consumer<Rect> onFinishResizeCallback) { + if (wct == null) { + return; + } + mResizeTransition = mTransitions.startTransition(TRANSIT_RESIZE_PIP, wct, this); + mFinishResizeCallback = onFinishResizeCallback; + } + @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @@ -126,43 +144,6 @@ public class PipTransition extends PipTransitionController { public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishT) {} - private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition, - @NonNull TransitionRequestInfo request) { - // cache the original task token to check for multi-activity case later - final ActivityManager.RunningTaskInfo pipTask = request.getPipTask(); - PictureInPictureParams pipParams = pipTask.pictureInPictureParams; - mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo, - pipParams, mPipBoundsAlgorithm); - - // calculate the entry bounds and notify core to move task to pinned with final bounds - final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); - mPipBoundsState.setBounds(entryBounds); - - WindowContainerTransaction wct = new WindowContainerTransaction(); - wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds); - return wct; - } - - private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) { - final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask(); - if (pipTask == null) { - return false; - } - if (pipTask.pictureInPictureParams == null) { - return false; - } - - // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type - // implies that we are entering PiP in button navigation mode. This is guaranteed by - // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav. - return requestInfo.getType() == TRANSIT_OPEN - && pipTask.pictureInPictureParams.isAutoEnterEnabled(); - } - - private boolean isEnterPictureInPictureModeRequest(@NonNull TransitionRequestInfo requestInfo) { - return requestInfo.getType() == TRANSIT_PIP; - } - @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @@ -182,16 +163,48 @@ public class PipTransition extends PipTransitionController { } else if (transition == mExitViaExpandTransition) { mExitViaExpandTransition = null; return startExpandAnimation(info, startTransaction, finishTransaction, finishCallback); + } else if (transition == mResizeTransition) { + mResizeTransition = null; + return startResizeAnimation(info, startTransaction, finishTransaction, finishCallback); } return false; } - private boolean isLegacyEnter(@NonNull TransitionInfo info) { + private boolean startResizeAnimation(@NonNull TransitionInfo info, + @NonNull SurfaceControl.Transaction startTransaction, + @NonNull SurfaceControl.Transaction finishTransaction, + @NonNull Transitions.TransitionFinishCallback finishCallback) { TransitionInfo.Change pipChange = getPipChange(info); - // If the only change in the changes list is a TO_FRONT mode PiP task, - // then this is legacy-enter PiP. - return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT - && info.getChanges().size() == 1; + if (pipChange == null) { + return false; + } + SurfaceControl pipLeash = pipChange.getLeash(); + Rect destinationBounds = pipChange.getEndAbsBounds(); + + // Even though the final bounds and crop are applied with finishTransaction since + // this is a visible change, we still need to handle the app draw coming in. Snapshot + // covering app draw during collection will be removed by startTransaction. So we make + // the crop equal to the final bounds and then scale the leash back to starting bounds. + startTransaction.setWindowCrop(pipLeash, pipChange.getEndAbsBounds().width(), + pipChange.getEndAbsBounds().height()); + startTransaction.setScale(pipLeash, + (float) mPipBoundsState.getBounds().width() / destinationBounds.width(), + (float) mPipBoundsState.getBounds().height() / destinationBounds.height()); + startTransaction.apply(); + + finishTransaction.setScale(pipLeash, + (float) mPipBoundsState.getBounds().width() / destinationBounds.width(), + (float) mPipBoundsState.getBounds().height() / destinationBounds.height()); + + // We are done with the transition, but will continue animating leash to final bounds. + finishCallback.onTransitionFinished(null); + + // Animate the pip leash with the new buffer + final int duration = mContext.getResources().getInteger( + R.integer.config_pipResizeAnimationDuration); + // TODO: b/275910498 Couple this routine with a new implementation of the PiP animator. + startResizeAnimation(pipLeash, mPipBoundsState.getBounds(), destinationBounds, duration); + return true; } private boolean startBoundsTypeEnterAnimation(@NonNull TransitionInfo info, @@ -251,6 +264,57 @@ public class PipTransition extends PipTransitionController { return null; } + private WindowContainerTransaction getEnterPipTransaction(@NonNull IBinder transition, + @NonNull TransitionRequestInfo request) { + // cache the original task token to check for multi-activity case later + final ActivityManager.RunningTaskInfo pipTask = request.getPipTask(); + PictureInPictureParams pipParams = pipTask.pictureInPictureParams; + mPipBoundsState.setBoundsStateForEntry(pipTask.topActivity, pipTask.topActivityInfo, + pipParams, mPipBoundsAlgorithm); + + // calculate the entry bounds and notify core to move task to pinned with final bounds + final Rect entryBounds = mPipBoundsAlgorithm.getEntryDestinationBounds(); + mPipBoundsState.setBounds(entryBounds); + + WindowContainerTransaction wct = new WindowContainerTransaction(); + wct.movePipActivityToPinnedRootTask(pipTask.token, entryBounds); + return wct; + } + + private boolean isAutoEnterInButtonNavigation(@NonNull TransitionRequestInfo requestInfo) { + final ActivityManager.RunningTaskInfo pipTask = requestInfo.getPipTask(); + if (pipTask == null) { + return false; + } + if (pipTask.pictureInPictureParams == null) { + return false; + } + + // Assuming auto-enter is enabled and pipTask is non-null, the TRANSIT_OPEN request type + // implies that we are entering PiP in button navigation mode. This is guaranteed by + // TaskFragment#startPausing()` in Core which wouldn't get called in gesture nav. + return requestInfo.getType() == TRANSIT_OPEN + && pipTask.pictureInPictureParams.isAutoEnterEnabled(); + } + + private boolean isEnterPictureInPictureModeRequest(@NonNull TransitionRequestInfo requestInfo) { + return requestInfo.getType() == TRANSIT_PIP; + } + + private boolean isLegacyEnter(@NonNull TransitionInfo info) { + TransitionInfo.Change pipChange = getPipChange(info); + // If the only change in the changes list is a TO_FRONT mode PiP task, + // then this is legacy-enter PiP. + return pipChange != null && pipChange.getMode() == TRANSIT_TO_FRONT + && info.getChanges().size() == 1; + } + + /** + * TODO: b/275910498 Use a new implementation of the PiP animator here. + */ + private void startResizeAnimation(SurfaceControl leash, Rect startBounds, + Rect endBounds, int duration) {} + private void onExitPip() { mPipTaskToken = null; mPipScheduler.onExitPip(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index b0d8b47b170a..f7b4214f7612 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -172,6 +172,9 @@ public class Transitions implements RemoteCallable<Transitions>, /** Transition to animate task to desktop. */ public static final int TRANSIT_MOVE_TO_DESKTOP = WindowManager.TRANSIT_FIRST_CUSTOM + 15; + /** Transition to resize PiP task. */ + public static final int TRANSIT_RESIZE_PIP = TRANSIT_FIRST_CUSTOM + 16; + private final ShellTaskOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java index 0f8db85dcef4..b583acda1c9a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipDoubleTapHelperTest.java @@ -16,10 +16,10 @@ package com.android.wm.shell.pip.phone; -import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_CUSTOM; -import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_DEFAULT; -import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.SIZE_SPEC_MAX; -import static com.android.wm.shell.pip.phone.PipDoubleTapHelper.nextSizeSpec; +import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_CUSTOM; +import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_DEFAULT; +import static com.android.wm.shell.common.pip.PipDoubleTapHelper.SIZE_SPEC_MAX; +import static com.android.wm.shell.common.pip.PipDoubleTapHelper.nextSizeSpec; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -30,6 +30,7 @@ import android.testing.AndroidTestingRunner; import com.android.wm.shell.ShellTestCase; import com.android.wm.shell.common.pip.PipBoundsState; +import com.android.wm.shell.common.pip.PipDoubleTapHelper; import org.junit.Assert; import org.junit.Before; @@ -38,7 +39,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; /** - * Unit test against {@link PipDoubleTapHelper}. + * Unit test against {@link com.android.wm.shell.common.pip.PipDoubleTapHelper}. */ @RunWith(AndroidTestingRunner.class) public class PipDoubleTapHelperTest extends ShellTestCase { |