• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.quickstep.util;
17 
18 import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
19 import static com.android.launcher3.states.RotationHelper.deltaRotation;
20 import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TRANSLATE;
21 import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
22 import static com.android.quickstep.util.RecentsOrientedState.preDisplayRotation;
23 import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
24 
25 import android.animation.TimeInterpolator;
26 import android.content.Context;
27 import android.content.res.Resources;
28 import android.graphics.Matrix;
29 import android.graphics.Point;
30 import android.graphics.PointF;
31 import android.graphics.Rect;
32 import android.graphics.RectF;
33 
34 import androidx.annotation.NonNull;
35 
36 import com.android.launcher3.DeviceProfile;
37 import com.android.launcher3.Utilities;
38 import com.android.launcher3.anim.PendingAnimation;
39 import com.android.launcher3.util.TraceHelper;
40 import com.android.quickstep.AnimatedFloat;
41 import com.android.quickstep.BaseActivityInterface;
42 import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
43 import com.android.quickstep.views.TaskView.FullscreenDrawParams;
44 import com.android.systemui.shared.recents.model.ThumbnailData;
45 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
46 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
47 
48 /**
49  * A utility class which emulates the layout behavior of TaskView and RecentsView
50  */
51 public class TaskViewSimulator implements TransformParams.BuilderProxy {
52 
53     private final Rect mTmpCropRect = new Rect();
54     private final RectF mTempRectF = new RectF();
55     private final float[] mTempPoint = new float[2];
56 
57     private final Context mContext;
58     private final BaseActivityInterface mSizeStrategy;
59     private final boolean mIsForLiveTile;
60 
61     @NonNull
62     private RecentsOrientedState mOrientationState;
63     private final boolean mIsRecentsRtl;
64 
65     private final Rect mTaskRect = new Rect();
66     private boolean mDrawsBelowRecents;
67     private final PointF mPivot = new PointF();
68     private DeviceProfile mDp;
69 
70     private final Matrix mMatrix = new Matrix();
71     private final Matrix mMatrixTmp = new Matrix();
72     private final Point mRunningTargetWindowPosition = new Point();
73 
74     // Thumbnail view properties
75     private final Rect mThumbnailPosition = new Rect();
76     private final ThumbnailData mThumbnailData = new ThumbnailData();
77     private final PreviewPositionHelper mPositionHelper = new PreviewPositionHelper();
78     private final Matrix mInversePositionMatrix = new Matrix();
79 
80     // TaskView properties
81     private final FullscreenDrawParams mCurrentFullscreenParams;
82     public final AnimatedFloat taskPrimaryTranslation = new AnimatedFloat();
83     public final AnimatedFloat taskSecondaryTranslation = new AnimatedFloat();
84 
85     // RecentsView properties
86     public final AnimatedFloat recentsViewScale = new AnimatedFloat();
87     public final AnimatedFloat fullScreenProgress = new AnimatedFloat();
88     public final AnimatedFloat recentsViewSecondaryTranslation = new AnimatedFloat();
89     public final AnimatedFloat recentsViewPrimaryTranslation = new AnimatedFloat();
90     public final AnimatedFloat recentsViewScroll = new AnimatedFloat();
91 
92     // Cached calculations
93     private boolean mLayoutValid = false;
94     private int mOrientationStateId;
95 
TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy)96     public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
97         this(context, sizeStrategy, false);
98     }
99 
TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy, boolean isForLiveTile)100     public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy,
101             boolean isForLiveTile) {
102         mContext = context;
103         mSizeStrategy = sizeStrategy;
104         mIsForLiveTile = isForLiveTile;
105 
106         // TODO(b/187074722): Don't create this per-TaskViewSimulator
107         mOrientationState = TraceHelper.allowIpcs("",
108                 () -> new RecentsOrientedState(context, sizeStrategy, i -> { }));
109         mOrientationState.setGestureActive(true);
110         mCurrentFullscreenParams = new FullscreenDrawParams(context);
111         mOrientationStateId = mOrientationState.getStateId();
112         Resources resources = context.getResources();
113         mIsRecentsRtl = mOrientationState.getOrientationHandler().getRecentsRtlSetting(resources);
114     }
115 
116     /**
117      * Sets the device profile for the current state
118      */
setDp(DeviceProfile dp)119     public void setDp(DeviceProfile dp) {
120         mDp = dp;
121         mLayoutValid = false;
122         mOrientationState.setDeviceProfile(dp);
123     }
124 
125     /**
126      * Sets the orientation state used for this animation
127      */
setOrientationState(@onNull RecentsOrientedState orientationState)128     public void setOrientationState(@NonNull RecentsOrientedState orientationState) {
129         mOrientationState = orientationState;
130         mLayoutValid = false;
131     }
132 
133     /**
134      * @see com.android.quickstep.views.RecentsView#FULLSCREEN_PROGRESS
135      */
getFullScreenScale()136     public float getFullScreenScale() {
137         if (mDp == null) {
138             return 1;
139         }
140         mSizeStrategy.calculateTaskSize(mContext, mDp, mTaskRect,
141                 mOrientationState.getOrientationHandler());
142         return mOrientationState.getFullScreenScaleAndPivot(mTaskRect, mDp, mPivot);
143     }
144 
145     /**
146      * Sets the targets which the simulator will control
147      */
setPreview(RemoteAnimationTargetCompat runningTarget)148     public void setPreview(RemoteAnimationTargetCompat runningTarget) {
149         setPreviewBounds(runningTarget.screenSpaceBounds, runningTarget.contentInsets);
150         mRunningTargetWindowPosition.set(runningTarget.screenSpaceBounds.left,
151                 runningTarget.screenSpaceBounds.top);
152     }
153 
154     /**
155      * Sets the targets which the simulator will control
156      */
setPreviewBounds(Rect bounds, Rect insets)157     public void setPreviewBounds(Rect bounds, Rect insets) {
158         mThumbnailData.insets.set(insets);
159         // TODO: What is this?
160         mThumbnailData.windowingMode = WINDOWING_MODE_FULLSCREEN;
161 
162         mThumbnailPosition.set(bounds);
163         mLayoutValid = false;
164     }
165 
166     /**
167      * Updates the scroll for RecentsView
168      */
setScroll(float scroll)169     public void setScroll(float scroll) {
170         recentsViewScroll.value = scroll;
171     }
172 
setDrawsBelowRecents(boolean drawsBelowRecents)173     public void setDrawsBelowRecents(boolean drawsBelowRecents) {
174         mDrawsBelowRecents = drawsBelowRecents;
175     }
176 
177     /**
178      * Adds animation for all the components corresponding to transition from an app to overview.
179      */
addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator)180     public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
181         pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
182         pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1, interpolator);
183     }
184 
185     /**
186      * Adds animation for all the components corresponding to transition from overview to the app.
187      */
addOverviewToAppAnim(PendingAnimation pa, TimeInterpolator interpolator)188     public void addOverviewToAppAnim(PendingAnimation pa, TimeInterpolator interpolator) {
189         pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 0, 1, interpolator);
190         pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, 1, getFullScreenScale(), interpolator);
191     }
192 
193     /**
194      * Returns the current clipped/visible window bounds in the window coordinate space
195      */
getCurrentCropRect()196     public RectF getCurrentCropRect() {
197         // Crop rect is the inverse of thumbnail matrix
198         RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
199         mTempRectF.set(-insets.left, -insets.top,
200                 mTaskRect.width() + insets.right, mTaskRect.height() + insets.bottom);
201         mInversePositionMatrix.mapRect(mTempRectF);
202         return mTempRectF;
203     }
204 
205     /**
206      * Returns the current task bounds in the Launcher coordinate space.
207      */
getCurrentRect()208     public RectF getCurrentRect() {
209         RectF result = getCurrentCropRect();
210         mMatrixTmp.set(mMatrix);
211         preDisplayRotation(mOrientationState.getDisplayRotation(), mDp.widthPx, mDp.heightPx,
212                 mMatrixTmp);
213         mMatrixTmp.mapRect(result);
214         return result;
215     }
216 
getOrientationState()217     public RecentsOrientedState getOrientationState() {
218         return mOrientationState;
219     }
220 
221     /**
222      * Returns the current transform applied to the window
223      */
getCurrentMatrix()224     public Matrix getCurrentMatrix() {
225         return mMatrix;
226     }
227 
228     /**
229      * Applies the rotation on the matrix to so that it maps from launcher coordinate space to
230      * window coordinate space.
231      */
applyWindowToHomeRotation(Matrix matrix)232     public void applyWindowToHomeRotation(Matrix matrix) {
233         mMatrix.postTranslate(mDp.windowX, mDp.windowY);
234         postDisplayRotation(deltaRotation(
235                 mOrientationState.getRecentsActivityRotation(),
236                 mOrientationState.getDisplayRotation()),
237                 mDp.widthPx, mDp.heightPx, matrix);
238         matrix.postTranslate(-mRunningTargetWindowPosition.x, -mRunningTargetWindowPosition.y);
239     }
240 
241     /**
242      * Applies the target to the previously set parameters
243      */
apply(TransformParams params)244     public void apply(TransformParams params) {
245         if (mDp == null || mThumbnailPosition.isEmpty()) {
246             return;
247         }
248         if (!mLayoutValid || mOrientationStateId != mOrientationState.getStateId()) {
249             mLayoutValid = true;
250             mOrientationStateId = mOrientationState.getStateId();
251 
252             getFullScreenScale();
253             mThumbnailData.rotation = mOrientationState.getDisplayRotation();
254 
255             // mIsRecentsRtl is the inverse of TaskView RTL.
256             boolean isRtlEnabled = !mIsRecentsRtl;
257             mPositionHelper.updateThumbnailMatrix(
258                     mThumbnailPosition, mThumbnailData,
259                     mTaskRect.width(), mTaskRect.height(),
260                     mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
261             mPositionHelper.getMatrix().invert(mInversePositionMatrix);
262         }
263 
264         float fullScreenProgress = Utilities.boundToRange(this.fullScreenProgress.value, 0, 1);
265         mCurrentFullscreenParams.setProgress(
266                 fullScreenProgress, recentsViewScale.value, mTaskRect.width(), mDp,
267                 mPositionHelper);
268 
269         // Apply thumbnail matrix
270         RectF insets = mCurrentFullscreenParams.mCurrentDrawnInsets;
271         float scale = mCurrentFullscreenParams.mScale;
272         float taskWidth = mTaskRect.width();
273         float taskHeight = mTaskRect.height();
274 
275         mMatrix.set(mPositionHelper.getMatrix());
276         mMatrix.postTranslate(insets.left, insets.top);
277         mMatrix.postScale(scale, scale);
278 
279         // Apply TaskView matrix: translate, scroll
280         mMatrix.postTranslate(mTaskRect.left, mTaskRect.top);
281         mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
282                 taskPrimaryTranslation.value);
283         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
284                 taskSecondaryTranslation.value);
285         mOrientationState.getOrientationHandler().set(
286                 mMatrix, MATRIX_POST_TRANSLATE, recentsViewScroll.value);
287 
288         // Apply RecentsView matrix
289         mMatrix.postScale(recentsViewScale.value, recentsViewScale.value, mPivot.x, mPivot.y);
290         mOrientationState.getOrientationHandler().setSecondary(mMatrix, MATRIX_POST_TRANSLATE,
291                 recentsViewSecondaryTranslation.value);
292         mOrientationState.getOrientationHandler().set(mMatrix, MATRIX_POST_TRANSLATE,
293                 recentsViewPrimaryTranslation.value);
294         applyWindowToHomeRotation(mMatrix);
295 
296         // Crop rect is the inverse of thumbnail matrix
297         mTempRectF.set(-insets.left, -insets.top,
298                 taskWidth + insets.right, taskHeight + insets.bottom);
299         mInversePositionMatrix.mapRect(mTempRectF);
300         mTempRectF.roundOut(mTmpCropRect);
301 
302         params.applySurfaceParams(params.createSurfaceParams(this));
303     }
304 
305     @Override
onBuildTargetParams( Builder builder, RemoteAnimationTargetCompat app, TransformParams params)306     public void onBuildTargetParams(
307             Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
308         builder.withMatrix(mMatrix)
309                 .withWindowCrop(mTmpCropRect)
310                 .withCornerRadius(getCurrentCornerRadius());
311 
312         if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mIsForLiveTile
313                 && params.getRecentsSurface() != null) {
314             // When relativeLayer = 0, it reverts the surfaces back to the original order.
315             builder.withRelativeLayerTo(params.getRecentsSurface(),
316                     mDrawsBelowRecents ? Integer.MIN_VALUE : 0);
317         }
318     }
319 
320     /**
321      * Returns the corner radius that should be applied to the target so that it matches the
322      * TaskView
323      */
getCurrentCornerRadius()324     public float getCurrentCornerRadius() {
325         float visibleRadius = mCurrentFullscreenParams.mCurrentDrawnCornerRadius;
326         mTempPoint[0] = visibleRadius;
327         mTempPoint[1] = 0;
328         mInversePositionMatrix.mapVectors(mTempPoint);
329 
330         // Ideally we should use square-root. This is an optimization as one of the dimension is 0.
331         return Math.max(Math.abs(mTempPoint[0]), Math.abs(mTempPoint[1]));
332     }
333 
334 }
335