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