• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.wm.shell.desktopmode;
18 
19 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
20 
21 import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU;
22 import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.getEnterTransitionType;
23 import static com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isEnterDesktopModeTransition;
24 
25 import android.animation.Animator;
26 import android.animation.AnimatorListenerAdapter;
27 import android.animation.RectEvaluator;
28 import android.animation.ValueAnimator;
29 import android.app.ActivityManager;
30 import android.graphics.Rect;
31 import android.os.IBinder;
32 import android.util.Slog;
33 import android.view.SurfaceControl;
34 import android.view.WindowManager;
35 import android.view.WindowManager.TransitionType;
36 import android.window.TransitionInfo;
37 import android.window.TransitionRequestInfo;
38 import android.window.WindowContainerTransaction;
39 
40 import androidx.annotation.NonNull;
41 import androidx.annotation.Nullable;
42 
43 import com.android.internal.jank.InteractionJankMonitor;
44 import com.android.internal.util.LatencyTracker;
45 import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource;
46 import com.android.wm.shell.transition.Transitions;
47 import com.android.wm.shell.windowdecor.OnTaskResizeAnimationListener;
48 
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.function.Supplier;
52 
53 /**
54  * The {@link Transitions.TransitionHandler} that handles transitions for desktop mode tasks
55  * entering and exiting freeform.
56  */
57 public class EnterDesktopTaskTransitionHandler implements Transitions.TransitionHandler {
58 
59     private static final String TAG = "EnterDesktopTaskTransitionHandler";
60     private final Transitions mTransitions;
61     private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;
62 
63     public static final int FREEFORM_ANIMATION_DURATION = 336;
64 
65     private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
66     private final InteractionJankMonitor mInteractionJankMonitor;
67     private final LatencyTracker mLatencyTracker;
68 
69     private OnTaskResizeAnimationListener mOnTaskResizeAnimationListener;
70 
EnterDesktopTaskTransitionHandler( Transitions transitions, InteractionJankMonitor interactionJankMonitor, LatencyTracker latencyTracker)71     public EnterDesktopTaskTransitionHandler(
72             Transitions transitions,
73             InteractionJankMonitor interactionJankMonitor,
74             LatencyTracker latencyTracker) {
75         this(transitions, interactionJankMonitor, latencyTracker, SurfaceControl.Transaction::new);
76     }
77 
EnterDesktopTaskTransitionHandler( Transitions transitions, InteractionJankMonitor interactionJankMonitor, LatencyTracker latencyTracker, Supplier<SurfaceControl.Transaction> supplier)78     public EnterDesktopTaskTransitionHandler(
79             Transitions transitions,
80             InteractionJankMonitor interactionJankMonitor,
81             LatencyTracker latencyTracker,
82             Supplier<SurfaceControl.Transaction> supplier) {
83         mTransitions = transitions;
84         mInteractionJankMonitor = interactionJankMonitor;
85         mLatencyTracker = latencyTracker;
86         mTransactionSupplier = supplier;
87     }
88 
setOnTaskResizeAnimationListener(OnTaskResizeAnimationListener listener)89     void setOnTaskResizeAnimationListener(OnTaskResizeAnimationListener listener) {
90         mOnTaskResizeAnimationListener = listener;
91     }
92 
93     /**
94      * Starts Transition of type TRANSIT_MOVE_TO_DESKTOP
95      *
96      * @param wct WindowContainerTransaction for transition
97      * @return the token representing the started transition
98      */
moveToDesktop( @onNull WindowContainerTransaction wct, DesktopModeTransitionSource transitionSource )99     public IBinder moveToDesktop(
100             @NonNull WindowContainerTransaction wct,
101             DesktopModeTransitionSource transitionSource
102     ) {
103         final IBinder token = mTransitions.startTransition(getEnterTransitionType(transitionSource),
104                 wct, this);
105         mPendingTransitionTokens.add(token);
106         return token;
107     }
108 
109     @Override
startAnimation(@onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull Transitions.TransitionFinishCallback finishCallback)110     public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
111             @NonNull SurfaceControl.Transaction startT,
112             @NonNull SurfaceControl.Transaction finishT,
113             @NonNull Transitions.TransitionFinishCallback finishCallback) {
114         boolean transitionHandled = false;
115         for (TransitionInfo.Change change : info.getChanges()) {
116             if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
117                 continue;
118             }
119 
120             final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
121             if (taskInfo == null || taskInfo.taskId == -1) {
122                 continue;
123             }
124 
125             if (change.getMode() == WindowManager.TRANSIT_CHANGE) {
126                 transitionHandled |= startChangeTransition(
127                         transition, info.getType(), change, startT, finishT, finishCallback);
128             }
129         }
130 
131         if (transitionHandled
132                 && info.getType()
133                         == DesktopModeTransitionTypes
134                                 .TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON) {
135             mLatencyTracker.onActionEnd(LatencyTracker.ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU);
136         }
137 
138         mPendingTransitionTokens.remove(transition);
139 
140         return transitionHandled;
141     }
142 
startChangeTransition( @onNull IBinder transition, @TransitionType int type, @NonNull TransitionInfo.Change change, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull Transitions.TransitionFinishCallback finishCallback)143     private boolean startChangeTransition(
144             @NonNull IBinder transition,
145             @TransitionType int type,
146             @NonNull TransitionInfo.Change change,
147             @NonNull SurfaceControl.Transaction startT,
148             @NonNull SurfaceControl.Transaction finishT,
149             @NonNull Transitions.TransitionFinishCallback finishCallback) {
150         if (!mPendingTransitionTokens.contains(transition)) {
151             return false;
152         }
153 
154         final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
155         if (isEnterDesktopModeTransition(type)
156                 && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
157             return animateMoveToDesktop(change, startT, finishCallback);
158         }
159 
160         return false;
161     }
162 
animateMoveToDesktop( @onNull TransitionInfo.Change change, @NonNull SurfaceControl.Transaction startT, @NonNull Transitions.TransitionFinishCallback finishCallback)163     private boolean animateMoveToDesktop(
164             @NonNull TransitionInfo.Change change,
165             @NonNull SurfaceControl.Transaction startT,
166             @NonNull Transitions.TransitionFinishCallback finishCallback) {
167         final SurfaceControl leash = change.getLeash();
168         final Rect startBounds = change.getStartAbsBounds();
169         final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
170         if (mOnTaskResizeAnimationListener == null) {
171             Slog.e(TAG, "onTaskResizeAnimationListener is not available for this transition");
172             return false;
173         }
174 
175         startT.setPosition(leash, startBounds.left, startBounds.top)
176                 .setWindowCrop(leash, startBounds.width(), startBounds.height())
177                 .show(leash);
178         mOnTaskResizeAnimationListener.onAnimationStart(taskInfo.taskId, startT, startBounds);
179         final ValueAnimator animator = ValueAnimator.ofObject(new RectEvaluator(),
180                 change.getStartAbsBounds(), change.getEndAbsBounds());
181         animator.setDuration(FREEFORM_ANIMATION_DURATION);
182         SurfaceControl.Transaction t = mTransactionSupplier.get();
183         animator.addUpdateListener(animation -> {
184             final Rect animationValue = (Rect) animator.getAnimatedValue();
185             t.setPosition(leash, animationValue.left, animationValue.top)
186                     .setWindowCrop(leash, animationValue.width(), animationValue.height())
187                     .show(leash);
188             mOnTaskResizeAnimationListener.onBoundsChange(taskInfo.taskId, t, animationValue);
189         });
190         animator.addListener(new AnimatorListenerAdapter() {
191             @Override
192             public void onAnimationEnd(Animator animation) {
193                 mOnTaskResizeAnimationListener.onAnimationEnd(taskInfo.taskId);
194                 mTransitions.getMainExecutor().execute(
195                         () -> finishCallback.onTransitionFinished(null));
196                 mInteractionJankMonitor.end(CUJ_DESKTOP_MODE_ENTER_MODE_APP_HANDLE_MENU);
197             }
198         });
199         animator.start();
200         return true;
201     }
202 
203     @Nullable
204     @Override
handleRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request)205     public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
206             @NonNull TransitionRequestInfo request) {
207         return null;
208     }
209 }
210