Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +24 −0 Original line number Original line Diff line number Diff line Loading @@ -77,6 +77,8 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.recents.RecentsTransitionHandler; Loading Loading @@ -561,6 +563,28 @@ public abstract class WMShellBaseModule { return new TaskViewTransitions(transitions); return new TaskViewTransitions(transitions); } } // // Keyguard transitions (optional feature) // @WMSingleton @Provides static KeyguardTransitionHandler provideKeyguardTransitionHandler( ShellInit shellInit, Transitions transitions, @ShellMainThread Handler mainHandler, @ShellMainThread ShellExecutor mainExecutor) { return new KeyguardTransitionHandler( shellInit, transitions, mainHandler, mainExecutor); } @WMSingleton @Provides static KeyguardTransitions provideKeyguardTransitions( KeyguardTransitionHandler handler) { return handler.asKeyguardTransitions(); } // // // Display areas // Display areas // // Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.Pip; Loading Loading @@ -532,9 +533,10 @@ public abstract class WMShellModule { Optional<SplitScreenController> splitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<RecentsTransitionHandler> recentsTransitionHandler, Optional<RecentsTransitionHandler> recentsTransitionHandler, KeyguardTransitionHandler keyguardTransitionHandler, Transitions transitions) { Transitions transitions) { return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, pipTouchHandlerOptional, recentsTransitionHandler); pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler); } } @WMSingleton @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java 0 → 100644 +274 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.keyguard; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_SLEEP; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; import static com.android.wm.shell.util.TransitionUtil.isOpeningType; import static com.android.wm.shell.util.TransitionUtil.isClosingType; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; import java.util.Map; /** * The handler for Keyguard enter/exit and occlude/unocclude animations. * * <p>This takes the highest priority. */ public class KeyguardTransitionHandler implements Transitions.TransitionHandler { private static final String TAG = "KeyguardTransition"; private final Transitions mTransitions; private final Handler mMainHandler; private final ShellExecutor mMainExecutor; private final Map<IBinder, IRemoteTransition> mStartedTransitions = new ArrayMap<>(); /** * Local IRemoteTransition implementations registered by the keyguard service. * @see KeyguardTransitions */ private IRemoteTransition mExitTransition = null; private IRemoteTransition mOccludeTransition = null; private IRemoteTransition mOccludeByDreamTransition = null; private IRemoteTransition mUnoccludeTransition = null; public KeyguardTransitionHandler( @NonNull ShellInit shellInit, @NonNull Transitions transitions, @NonNull Handler mainHandler, @NonNull ShellExecutor mainExecutor) { mTransitions = transitions; mMainHandler = mainHandler; mMainExecutor = mainExecutor; shellInit.addInitCallback(this::onInit, this); } private void onInit() { mTransitions.addHandler(this); } /** * Interface for SystemUI implementations to set custom Keyguard exit/occlude handlers. */ @ExternalThread public KeyguardTransitions asKeyguardTransitions() { return new KeyguardTransitionsImpl(); } public static boolean handles(TransitionInfo info) { return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0 || (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0 || info.getType() == TRANSIT_KEYGUARD_OCCLUDE || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE; } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { if (!handles(info)) { return false; } boolean hasOpeningOcclude = false; boolean hasOpeningDream = false; boolean hasClosingApp = false; // Check for occluding/dream/closing apps for (int i = info.getChanges().size() - 1; i >= 0; i--) { final TransitionInfo.Change change = info.getChanges().get(i); if (isOpeningType(change.getMode())) { if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) { hasOpeningOcclude = true; } if (change.getTaskInfo() != null && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM) { hasOpeningDream = true; } } else if (isClosingType(change.getMode())) { hasClosingApp = true; } } // Choose a transition applicable for the changes and keyguard state. if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { return startAnimation(mExitTransition, "going-away", transition, info, startTransaction, finishTransaction, finishCallback); } if (hasOpeningOcclude || info.getType() == TRANSIT_KEYGUARD_OCCLUDE) { if (hasOpeningDream) { return startAnimation(mOccludeByDreamTransition, "occlude-by-dream", transition, info, startTransaction, finishTransaction, finishCallback); } else { return startAnimation(mOccludeTransition, "occlude", transition, info, startTransaction, finishTransaction, finishCallback); } } else if (hasClosingApp || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) { return startAnimation(mUnoccludeTransition, "unocclude", transition, info, startTransaction, finishTransaction, finishCallback); } else { Log.wtf(TAG, "Failed to play: " + info); return false; } } private boolean startAnimation(IRemoteTransition remoteHandler, String description, @NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "start keyguard %s transition, info = %s", description, info); try { remoteHandler.startAnimation(transition, info, startTransaction, new IRemoteTransitionFinishedCallback.Stub() { @Override public void onTransitionFinished( WindowContainerTransaction wct, SurfaceControl.Transaction sct) { mMainExecutor.execute(() -> { finishCallback.onTransitionFinished(wct, null); }); } }); mStartedTransitions.put(transition, remoteHandler); } catch (RemoteException e) { Log.wtf(TAG, "RemoteException thrown from local IRemoteTransition", e); return false; } startTransaction.clear(); return true; } @Override public void mergeAnimation(@NonNull IBinder nextTransition, @NonNull TransitionInfo nextInfo, @NonNull SurfaceControl.Transaction nextT, @NonNull IBinder currentTransition, @NonNull TransitionFinishCallback nextFinishCallback) { final IRemoteTransition playing = mStartedTransitions.get(currentTransition); if (playing == null) { ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "unknown keyguard transition %s", currentTransition); return; } if (nextInfo.getType() == TRANSIT_SLEEP) { // An empty SLEEP transition comes in as a signal to abort transitions whenever a sleep // token is held. In cases where keyguard is showing, we are running the animation for // the device sleeping/waking, so it's best to ignore this and keep playing anyway. return; } else { finishAnimationImmediately(currentTransition); } } @Override public void onTransitionConsumed(IBinder transition, boolean aborted, SurfaceControl.Transaction finishTransaction) { finishAnimationImmediately(transition); } @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { return null; } private void finishAnimationImmediately(IBinder transition) { final IRemoteTransition playing = mStartedTransitions.get(transition); if (playing != null) { final IBinder fakeTransition = new Binder(); final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0); final SurfaceControl.Transaction fakeT = new SurfaceControl.Transaction(); final FakeFinishCallback fakeFinishCb = new FakeFinishCallback(); try { playing.mergeAnimation(fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb); } catch (RemoteException e) { // There is no good reason for this to happen because the player is a local object // implementing an AIDL interface. Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e); } } } private static class FakeFinishCallback extends IRemoteTransitionFinishedCallback.Stub { @Override public void onTransitionFinished( WindowContainerTransaction wct, SurfaceControl.Transaction t) { return; } } @ExternalThread private final class KeyguardTransitionsImpl implements KeyguardTransitions { @Override public void register( IRemoteTransition exitTransition, IRemoteTransition occludeTransition, IRemoteTransition occludeByDreamTransition, IRemoteTransition unoccludeTransition) { mMainExecutor.execute(() -> { mExitTransition = exitTransition; mOccludeTransition = occludeTransition; mOccludeByDreamTransition = occludeByDreamTransition; mUnoccludeTransition = unoccludeTransition; }); } } } libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java 0 → 100644 +41 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.keyguard; import android.annotation.NonNull; import android.window.IRemoteTransition; import com.android.wm.shell.common.annotations.ExternalThread; /** * Interface exposed to SystemUI Keyguard to register handlers for running * animations on keyguard visibility changes. * * TODO(b/274954192): Merge the occludeTransition and occludeByDream handlers and just let the * keyguard handler make the decision on which version it wants to play. */ @ExternalThread public interface KeyguardTransitions { /** * Registers a set of remote transitions for Keyguard. */ default void register( @NonNull IRemoteTransition unlockTransition, @NonNull IRemoteTransition occludeTransition, @NonNull IRemoteTransition occludeByDreamTransition, @NonNull IRemoteTransition unoccludeTransition) {} } libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +29 −1 Original line number Original line Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.protolog.ShellProtoLogGroup; Loading @@ -63,6 +64,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private PipTransitionController mPipHandler; private PipTransitionController mPipHandler; private RecentsTransitionHandler mRecentsHandler; private RecentsTransitionHandler mRecentsHandler; private StageCoordinator mSplitHandler; private StageCoordinator mSplitHandler; private final KeyguardTransitionHandler mKeyguardHandler; private static class MixedTransition { private static class MixedTransition { static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; Loading @@ -76,6 +78,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, /** Recents transition while split-screen foreground. */ /** Recents transition while split-screen foreground. */ static final int TYPE_RECENTS_DURING_SPLIT = 4; static final int TYPE_RECENTS_DURING_SPLIT = 4; /** Keyguard exit/occlude/unocclude transition. */ static final int TYPE_KEYGUARD = 5; /** The default animation for this mixed transition. */ /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; static final int ANIM_TYPE_DEFAULT = 0; Loading Loading @@ -126,8 +131,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player, public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player, Optional<SplitScreenController> splitScreenControllerOptional, Optional<SplitScreenController> splitScreenControllerOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<RecentsTransitionHandler> recentsHandlerOptional) { Optional<RecentsTransitionHandler> recentsHandlerOptional, KeyguardTransitionHandler keyguardHandler) { mPlayer = player; mPlayer = player; mKeyguardHandler = keyguardHandler; if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent() if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent() && splitScreenControllerOptional.isPresent()) { && splitScreenControllerOptional.isPresent()) { // Add after dependencies because it is higher priority // Add after dependencies because it is higher priority Loading Loading @@ -263,12 +270,26 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { @NonNull Transitions.TransitionFinishCallback finishCallback) { MixedTransition mixed = null; MixedTransition mixed = null; for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { if (mActiveTransitions.get(i).mTransition != transition) continue; if (mActiveTransitions.get(i).mTransition != transition) continue; mixed = mActiveTransitions.get(i); mixed = mActiveTransitions.get(i); break; break; } } // Offer Keyguard the opportunity to take over lock transitions - ideally we could know by // the time of handleRequest, but we need more information than is available at that time. if (KeyguardTransitionHandler.handles(info)) { if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Converting mixed transition into a keyguard transition"); onTransitionConsumed(transition, false, null); } mixed = new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition); mActiveTransitions.add(mixed); } if (mixed == null) return false; if (mixed == null) return false; if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { Loading @@ -282,6 +303,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, finishCallback); finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { return mKeyguardHandler.startAnimation( transition, info, startTransaction, finishTransaction, finishCallback); } else { } else { mActiveTransitions.remove(mixed); mActiveTransitions.remove(mixed); throw new IllegalStateException("Starting mixed animation without a known mixed type? " throw new IllegalStateException("Starting mixed animation without a known mixed type? " Loading Loading @@ -574,6 +598,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } } mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } else { } else { throw new IllegalStateException("Playing a mixed transition with unknown type? " throw new IllegalStateException("Playing a mixed transition with unknown type? " + mixed.mType); + mixed.mType); Loading @@ -597,6 +623,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); } } } } } } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +24 −0 Original line number Original line Diff line number Diff line Loading @@ -77,6 +77,8 @@ import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipSurfaceTransactionHelper; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.PipUiEventLogger; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.keyguard.KeyguardTransitions; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; import com.android.wm.shell.recents.RecentsTransitionHandler; Loading Loading @@ -561,6 +563,28 @@ public abstract class WMShellBaseModule { return new TaskViewTransitions(transitions); return new TaskViewTransitions(transitions); } } // // Keyguard transitions (optional feature) // @WMSingleton @Provides static KeyguardTransitionHandler provideKeyguardTransitionHandler( ShellInit shellInit, Transitions transitions, @ShellMainThread Handler mainHandler, @ShellMainThread ShellExecutor mainExecutor) { return new KeyguardTransitionHandler( shellInit, transitions, mainHandler, mainExecutor); } @WMSingleton @Provides static KeyguardTransitions provideKeyguardTransitions( KeyguardTransitionHandler handler) { return handler.asKeyguardTransitions(); } // // // Display areas // Display areas // // Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +3 −1 Original line number Original line Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.wm.shell.freeform.FreeformComponents; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskListener; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionHandler; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.freeform.FreeformTaskTransitionObserver; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.kidsmode.KidsModeTaskOrganizer; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.onehanded.OneHandedController; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.Pip; Loading Loading @@ -532,9 +533,10 @@ public abstract class WMShellModule { Optional<SplitScreenController> splitScreenOptional, Optional<SplitScreenController> splitScreenOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<RecentsTransitionHandler> recentsTransitionHandler, Optional<RecentsTransitionHandler> recentsTransitionHandler, KeyguardTransitionHandler keyguardTransitionHandler, Transitions transitions) { Transitions transitions) { return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, return new DefaultMixedHandler(shellInit, transitions, splitScreenOptional, pipTouchHandlerOptional, recentsTransitionHandler); pipTouchHandlerOptional, recentsTransitionHandler, keyguardTransitionHandler); } } @WMSingleton @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitionHandler.java 0 → 100644 +274 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.keyguard; import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_KEYGUARD_OCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_OPEN; import static android.view.WindowManager.TRANSIT_SLEEP; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_LOCKED; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY; import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD; import static com.android.wm.shell.util.TransitionUtil.isOpeningType; import static com.android.wm.shell.util.TransitionUtil.isClosingType; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.RemoteException; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.util.ArrayMap; import android.util.Log; import android.view.SurfaceControl; import android.window.IRemoteTransition; import android.window.IRemoteTransitionFinishedCallback; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.annotations.ExternalThread; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.sysui.ShellInit; import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.transition.Transitions.TransitionFinishCallback; import java.util.Map; /** * The handler for Keyguard enter/exit and occlude/unocclude animations. * * <p>This takes the highest priority. */ public class KeyguardTransitionHandler implements Transitions.TransitionHandler { private static final String TAG = "KeyguardTransition"; private final Transitions mTransitions; private final Handler mMainHandler; private final ShellExecutor mMainExecutor; private final Map<IBinder, IRemoteTransition> mStartedTransitions = new ArrayMap<>(); /** * Local IRemoteTransition implementations registered by the keyguard service. * @see KeyguardTransitions */ private IRemoteTransition mExitTransition = null; private IRemoteTransition mOccludeTransition = null; private IRemoteTransition mOccludeByDreamTransition = null; private IRemoteTransition mUnoccludeTransition = null; public KeyguardTransitionHandler( @NonNull ShellInit shellInit, @NonNull Transitions transitions, @NonNull Handler mainHandler, @NonNull ShellExecutor mainExecutor) { mTransitions = transitions; mMainHandler = mainHandler; mMainExecutor = mainExecutor; shellInit.addInitCallback(this::onInit, this); } private void onInit() { mTransitions.addHandler(this); } /** * Interface for SystemUI implementations to set custom Keyguard exit/occlude handlers. */ @ExternalThread public KeyguardTransitions asKeyguardTransitions() { return new KeyguardTransitionsImpl(); } public static boolean handles(TransitionInfo info) { return (info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0 || (info.getFlags() & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0 || info.getType() == TRANSIT_KEYGUARD_OCCLUDE || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE; } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { if (!handles(info)) { return false; } boolean hasOpeningOcclude = false; boolean hasOpeningDream = false; boolean hasClosingApp = false; // Check for occluding/dream/closing apps for (int i = info.getChanges().size() - 1; i >= 0; i--) { final TransitionInfo.Change change = info.getChanges().get(i); if (isOpeningType(change.getMode())) { if (change.hasFlags(FLAG_OCCLUDES_KEYGUARD)) { hasOpeningOcclude = true; } if (change.getTaskInfo() != null && change.getTaskInfo().getActivityType() == ACTIVITY_TYPE_DREAM) { hasOpeningDream = true; } } else if (isClosingType(change.getMode())) { hasClosingApp = true; } } // Choose a transition applicable for the changes and keyguard state. if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0) { return startAnimation(mExitTransition, "going-away", transition, info, startTransaction, finishTransaction, finishCallback); } if (hasOpeningOcclude || info.getType() == TRANSIT_KEYGUARD_OCCLUDE) { if (hasOpeningDream) { return startAnimation(mOccludeByDreamTransition, "occlude-by-dream", transition, info, startTransaction, finishTransaction, finishCallback); } else { return startAnimation(mOccludeTransition, "occlude", transition, info, startTransaction, finishTransaction, finishCallback); } } else if (hasClosingApp || info.getType() == TRANSIT_KEYGUARD_UNOCCLUDE) { return startAnimation(mUnoccludeTransition, "unocclude", transition, info, startTransaction, finishTransaction, finishCallback); } else { Log.wtf(TAG, "Failed to play: " + info); return false; } } private boolean startAnimation(IRemoteTransition remoteHandler, String description, @NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "start keyguard %s transition, info = %s", description, info); try { remoteHandler.startAnimation(transition, info, startTransaction, new IRemoteTransitionFinishedCallback.Stub() { @Override public void onTransitionFinished( WindowContainerTransaction wct, SurfaceControl.Transaction sct) { mMainExecutor.execute(() -> { finishCallback.onTransitionFinished(wct, null); }); } }); mStartedTransitions.put(transition, remoteHandler); } catch (RemoteException e) { Log.wtf(TAG, "RemoteException thrown from local IRemoteTransition", e); return false; } startTransaction.clear(); return true; } @Override public void mergeAnimation(@NonNull IBinder nextTransition, @NonNull TransitionInfo nextInfo, @NonNull SurfaceControl.Transaction nextT, @NonNull IBinder currentTransition, @NonNull TransitionFinishCallback nextFinishCallback) { final IRemoteTransition playing = mStartedTransitions.get(currentTransition); if (playing == null) { ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "unknown keyguard transition %s", currentTransition); return; } if (nextInfo.getType() == TRANSIT_SLEEP) { // An empty SLEEP transition comes in as a signal to abort transitions whenever a sleep // token is held. In cases where keyguard is showing, we are running the animation for // the device sleeping/waking, so it's best to ignore this and keep playing anyway. return; } else { finishAnimationImmediately(currentTransition); } } @Override public void onTransitionConsumed(IBinder transition, boolean aborted, SurfaceControl.Transaction finishTransaction) { finishAnimationImmediately(transition); } @Nullable @Override public WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request) { return null; } private void finishAnimationImmediately(IBinder transition) { final IRemoteTransition playing = mStartedTransitions.get(transition); if (playing != null) { final IBinder fakeTransition = new Binder(); final TransitionInfo fakeInfo = new TransitionInfo(TRANSIT_SLEEP, 0x0); final SurfaceControl.Transaction fakeT = new SurfaceControl.Transaction(); final FakeFinishCallback fakeFinishCb = new FakeFinishCallback(); try { playing.mergeAnimation(fakeTransition, fakeInfo, fakeT, transition, fakeFinishCb); } catch (RemoteException e) { // There is no good reason for this to happen because the player is a local object // implementing an AIDL interface. Log.wtf(TAG, "RemoteException thrown from KeyguardService transition", e); } } } private static class FakeFinishCallback extends IRemoteTransitionFinishedCallback.Stub { @Override public void onTransitionFinished( WindowContainerTransaction wct, SurfaceControl.Transaction t) { return; } } @ExternalThread private final class KeyguardTransitionsImpl implements KeyguardTransitions { @Override public void register( IRemoteTransition exitTransition, IRemoteTransition occludeTransition, IRemoteTransition occludeByDreamTransition, IRemoteTransition unoccludeTransition) { mMainExecutor.execute(() -> { mExitTransition = exitTransition; mOccludeTransition = occludeTransition; mOccludeByDreamTransition = occludeByDreamTransition; mUnoccludeTransition = unoccludeTransition; }); } } }
libs/WindowManager/Shell/src/com/android/wm/shell/keyguard/KeyguardTransitions.java 0 → 100644 +41 −0 Original line number Original line Diff line number Diff line /* * Copyright (C) 2023 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.wm.shell.keyguard; import android.annotation.NonNull; import android.window.IRemoteTransition; import com.android.wm.shell.common.annotations.ExternalThread; /** * Interface exposed to SystemUI Keyguard to register handlers for running * animations on keyguard visibility changes. * * TODO(b/274954192): Merge the occludeTransition and occludeByDream handlers and just let the * keyguard handler make the decision on which version it wants to play. */ @ExternalThread public interface KeyguardTransitions { /** * Registers a set of remote transitions for Keyguard. */ default void register( @NonNull IRemoteTransition unlockTransition, @NonNull IRemoteTransition occludeTransition, @NonNull IRemoteTransition occludeByDreamTransition, @NonNull IRemoteTransition unoccludeTransition) {} }
libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java +29 −1 Original line number Original line Diff line number Diff line Loading @@ -40,6 +40,7 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowContainerTransactionCallback; import com.android.internal.protolog.common.ProtoLog; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.keyguard.KeyguardTransitionHandler; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.pip.phone.PipTouchHandler; import com.android.wm.shell.protolog.ShellProtoLogGroup; import com.android.wm.shell.protolog.ShellProtoLogGroup; Loading @@ -63,6 +64,7 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, private PipTransitionController mPipHandler; private PipTransitionController mPipHandler; private RecentsTransitionHandler mRecentsHandler; private RecentsTransitionHandler mRecentsHandler; private StageCoordinator mSplitHandler; private StageCoordinator mSplitHandler; private final KeyguardTransitionHandler mKeyguardHandler; private static class MixedTransition { private static class MixedTransition { static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; static final int TYPE_ENTER_PIP_FROM_SPLIT = 1; Loading @@ -76,6 +78,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, /** Recents transition while split-screen foreground. */ /** Recents transition while split-screen foreground. */ static final int TYPE_RECENTS_DURING_SPLIT = 4; static final int TYPE_RECENTS_DURING_SPLIT = 4; /** Keyguard exit/occlude/unocclude transition. */ static final int TYPE_KEYGUARD = 5; /** The default animation for this mixed transition. */ /** The default animation for this mixed transition. */ static final int ANIM_TYPE_DEFAULT = 0; static final int ANIM_TYPE_DEFAULT = 0; Loading Loading @@ -126,8 +131,10 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player, public DefaultMixedHandler(@NonNull ShellInit shellInit, @NonNull Transitions player, Optional<SplitScreenController> splitScreenControllerOptional, Optional<SplitScreenController> splitScreenControllerOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<PipTouchHandler> pipTouchHandlerOptional, Optional<RecentsTransitionHandler> recentsHandlerOptional) { Optional<RecentsTransitionHandler> recentsHandlerOptional, KeyguardTransitionHandler keyguardHandler) { mPlayer = player; mPlayer = player; mKeyguardHandler = keyguardHandler; if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent() if (Transitions.ENABLE_SHELL_TRANSITIONS && pipTouchHandlerOptional.isPresent() && splitScreenControllerOptional.isPresent()) { && splitScreenControllerOptional.isPresent()) { // Add after dependencies because it is higher priority // Add after dependencies because it is higher priority Loading Loading @@ -263,12 +270,26 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback) { @NonNull Transitions.TransitionFinishCallback finishCallback) { MixedTransition mixed = null; MixedTransition mixed = null; for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { for (int i = mActiveTransitions.size() - 1; i >= 0; --i) { if (mActiveTransitions.get(i).mTransition != transition) continue; if (mActiveTransitions.get(i).mTransition != transition) continue; mixed = mActiveTransitions.get(i); mixed = mActiveTransitions.get(i); break; break; } } // Offer Keyguard the opportunity to take over lock transitions - ideally we could know by // the time of handleRequest, but we need more information than is available at that time. if (KeyguardTransitionHandler.handles(info)) { if (mixed != null && mixed.mType != MixedTransition.TYPE_KEYGUARD) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Converting mixed transition into a keyguard transition"); onTransitionConsumed(transition, false, null); } mixed = new MixedTransition(MixedTransition.TYPE_KEYGUARD, transition); mActiveTransitions.add(mixed); } if (mixed == null) return false; if (mixed == null) return false; if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { if (mixed.mType == MixedTransition.TYPE_ENTER_PIP_FROM_SPLIT) { Loading @@ -282,6 +303,9 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { } else if (mixed.mType == MixedTransition.TYPE_RECENTS_DURING_SPLIT) { return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, return animateRecentsDuringSplit(mixed, info, startTransaction, finishTransaction, finishCallback); finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { return mKeyguardHandler.startAnimation( transition, info, startTransaction, finishTransaction, finishCallback); } else { } else { mActiveTransitions.remove(mixed); mActiveTransitions.remove(mixed); throw new IllegalStateException("Starting mixed animation without a known mixed type? " throw new IllegalStateException("Starting mixed animation without a known mixed type? " Loading Loading @@ -574,6 +598,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, } } mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, mixed.mLeftoversHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); finishCallback); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { mKeyguardHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback); } else { } else { throw new IllegalStateException("Playing a mixed transition with unknown type? " throw new IllegalStateException("Playing a mixed transition with unknown type? " + mixed.mType); + mixed.mType); Loading @@ -597,6 +623,8 @@ public class DefaultMixedHandler implements Transitions.TransitionHandler, mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { } else if (mixed.mType == MixedTransition.TYPE_OPTIONS_REMOTE_AND_PIP_CHANGE) { mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); mixed.mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); } else if (mixed.mType == MixedTransition.TYPE_KEYGUARD) { mKeyguardHandler.onTransitionConsumed(transition, aborted, finishT); } } } } } }