1 /* 2 * Copyright (C) 2019 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; 17 18 import static android.content.Intent.EXTRA_COMPONENT_NAME; 19 import static android.content.Intent.EXTRA_USER; 20 21 import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT; 22 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION; 23 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE; 24 import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK; 25 import static com.android.launcher3.Utilities.createHomeIntent; 26 import static com.android.launcher3.anim.Interpolators.ACCEL; 27 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; 28 29 import android.animation.ObjectAnimator; 30 import android.annotation.TargetApi; 31 import android.app.ActivityManager; 32 import android.app.ActivityOptions; 33 import android.content.ActivityNotFoundException; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.graphics.Matrix; 37 import android.graphics.Rect; 38 import android.graphics.RectF; 39 import android.os.Build; 40 import android.os.Bundle; 41 import android.os.Handler; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.Message; 45 import android.os.Messenger; 46 import android.os.ParcelUuid; 47 import android.os.UserHandle; 48 import android.view.Surface; 49 import android.view.SurfaceControl; 50 import android.view.SurfaceControl.Transaction; 51 52 import androidx.annotation.NonNull; 53 54 import com.android.launcher3.DeviceProfile; 55 import com.android.launcher3.Utilities; 56 import com.android.launcher3.anim.AnimatorPlaybackController; 57 import com.android.launcher3.anim.PendingAnimation; 58 import com.android.launcher3.anim.SpringAnimationBuilder; 59 import com.android.quickstep.fallback.FallbackRecentsView; 60 import com.android.quickstep.fallback.RecentsState; 61 import com.android.quickstep.util.RectFSpringAnim; 62 import com.android.quickstep.util.TransformParams; 63 import com.android.quickstep.util.TransformParams.BuilderProxy; 64 import com.android.systemui.shared.recents.model.Task.TaskKey; 65 import com.android.systemui.shared.system.ActivityManagerWrapper; 66 import com.android.systemui.shared.system.InputConsumerController; 67 import com.android.systemui.shared.system.RemoteAnimationTargetCompat; 68 import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams; 69 70 import java.lang.ref.WeakReference; 71 import java.util.ArrayList; 72 import java.util.UUID; 73 import java.util.function.Consumer; 74 75 /** 76 * Handles the navigation gestures when a 3rd party launcher is the default home activity. 77 */ 78 @TargetApi(Build.VERSION_CODES.R) 79 public class FallbackSwipeHandler extends 80 AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView, RecentsState> { 81 82 /** 83 * Message used for receiving gesture nav contract information. We use a static messenger to 84 * avoid leaking too make binders in case the receiving launcher does not handle the contract 85 * properly. 86 */ 87 private static StaticMessageReceiver sMessageReceiver = null; 88 89 private FallbackHomeAnimationFactory mActiveAnimationFactory; 90 private final boolean mRunningOverHome; 91 92 private final Matrix mTmpMatrix = new Matrix(); 93 private float mMaxLauncherScale = 1; 94 FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState, TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs, boolean continuingLastGesture, InputConsumerController inputConsumer)95 public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState, 96 TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs, 97 boolean continuingLastGesture, InputConsumerController inputConsumer) { 98 super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs, 99 continuingLastGesture, inputConsumer); 100 101 mRunningOverHome = ActivityManagerWrapper.isHomeTask(mGestureState.getRunningTask()); 102 if (mRunningOverHome) { 103 runActionOnRemoteHandles(remoteTargetHandle -> 104 remoteTargetHandle.getTransformParams().setHomeBuilderProxy( 105 FallbackSwipeHandler.this::updateHomeActivityTransformDuringSwipeUp)); 106 } 107 } 108 109 @Override initTransitionEndpoints(DeviceProfile dp)110 protected void initTransitionEndpoints(DeviceProfile dp) { 111 super.initTransitionEndpoints(dp); 112 if (mRunningOverHome) { 113 // Full screen scale should be independent of remote target handle 114 mMaxLauncherScale = 1 / mRemoteTargetHandles[0].getTaskViewSimulator() 115 .getFullScreenScale(); 116 } 117 } 118 updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder, RemoteAnimationTargetCompat app, TransformParams params)119 private void updateHomeActivityTransformDuringSwipeUp(SurfaceParams.Builder builder, 120 RemoteAnimationTargetCompat app, TransformParams params) { 121 setHomeScaleAndAlpha(builder, app, mCurrentShift.value, 122 Utilities.boundToRange(1 - mCurrentShift.value, 0, 1)); 123 } 124 setHomeScaleAndAlpha(SurfaceParams.Builder builder, RemoteAnimationTargetCompat app, float verticalShift, float alpha)125 private void setHomeScaleAndAlpha(SurfaceParams.Builder builder, 126 RemoteAnimationTargetCompat app, float verticalShift, float alpha) { 127 float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale); 128 mTmpMatrix.setScale(scale, scale, 129 app.localBounds.exactCenterX(), app.localBounds.exactCenterY()); 130 builder.withMatrix(mTmpMatrix).withAlpha(alpha); 131 } 132 133 @Override createHomeAnimationFactory(ArrayList<IBinder> launchCookies, long duration, boolean isTargetTranslucent, boolean appCanEnterPip, RemoteAnimationTargetCompat runningTaskTarget)134 protected HomeAnimationFactory createHomeAnimationFactory(ArrayList<IBinder> launchCookies, 135 long duration, boolean isTargetTranslucent, boolean appCanEnterPip, 136 RemoteAnimationTargetCompat runningTaskTarget) { 137 mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration); 138 ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); 139 Intent intent = new Intent(mGestureState.getHomeIntent()); 140 mActiveAnimationFactory.addGestureContract(intent); 141 try { 142 mContext.startActivity(intent, options.toBundle()); 143 } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { 144 mContext.startActivity(createHomeIntent()); 145 } 146 return mActiveAnimationFactory; 147 } 148 149 @Override handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget)150 protected boolean handleTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTarget) { 151 if (mActiveAnimationFactory != null 152 && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) { 153 mActiveAnimationFactory = null; 154 return false; 155 } 156 157 return super.handleTaskAppeared(appearedTaskTarget); 158 } 159 160 @Override finishRecentsControllerToHome(Runnable callback)161 protected void finishRecentsControllerToHome(Runnable callback) { 162 mRecentsAnimationController.finish( 163 false /* toRecents */, callback, true /* sendUserLeaveHint */); 164 } 165 166 @Override switchToScreenshot()167 protected void switchToScreenshot() { 168 if (mRunningOverHome) { 169 // When the current task is home, then we don't need to capture anything 170 mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); 171 } else { 172 super.switchToScreenshot(); 173 } 174 } 175 176 @Override notifyGestureAnimationStartToRecents()177 protected void notifyGestureAnimationStartToRecents() { 178 if (mRunningOverHome) { 179 if (SysUINavigationMode.getMode(mContext).hasGestures) { 180 mRecentsView.onGestureAnimationStartOnHome( 181 new ActivityManager.RunningTaskInfo[]{mGestureState.getRunningTask()}); 182 } 183 } else { 184 super.notifyGestureAnimationStartToRecents(); 185 } 186 } 187 188 private class FallbackHomeAnimationFactory extends HomeAnimationFactory { 189 private final Rect mTempRect = new Rect(); 190 private final TransformParams mHomeAlphaParams = new TransformParams(); 191 private final AnimatedFloat mHomeAlpha; 192 193 private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat(); 194 private final AnimatedFloat mRecentsAlpha = new AnimatedFloat(); 195 196 private final RectF mTargetRect = new RectF(); 197 private SurfaceControl mSurfaceControl; 198 199 private final long mDuration; 200 201 private RectFSpringAnim mSpringAnim; FallbackHomeAnimationFactory(long duration)202 FallbackHomeAnimationFactory(long duration) { 203 mDuration = duration; 204 205 if (mRunningOverHome) { 206 mHomeAlpha = new AnimatedFloat(); 207 mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1); 208 mVerticalShiftForScale.value = mCurrentShift.value; 209 runActionOnRemoteHandles(remoteTargetHandle -> 210 remoteTargetHandle.getTransformParams().setHomeBuilderProxy( 211 FallbackHomeAnimationFactory.this 212 ::updateHomeActivityTransformDuringHomeAnim)); 213 } else { 214 mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha); 215 mHomeAlpha.value = 0; 216 runActionOnRemoteHandles(remoteTargetHandle -> 217 remoteTargetHandle.getTransformParams().setHomeBuilderProxy( 218 FallbackHomeAnimationFactory.this 219 ::updateHomeActivityTransformDuringHomeAnim)); 220 } 221 222 mRecentsAlpha.value = 1; 223 runActionOnRemoteHandles(remoteTargetHandle -> 224 remoteTargetHandle.getTransformParams().setHomeBuilderProxy( 225 FallbackHomeAnimationFactory.this 226 ::updateRecentsActivityTransformDuringHomeAnim)); 227 } 228 229 @NonNull 230 @Override getWindowTargetRect()231 public RectF getWindowTargetRect() { 232 if (mTargetRect.isEmpty()) { 233 mTargetRect.set(super.getWindowTargetRect()); 234 } 235 return mTargetRect; 236 } 237 updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder, RemoteAnimationTargetCompat app, TransformParams params)238 private void updateRecentsActivityTransformDuringHomeAnim(SurfaceParams.Builder builder, 239 RemoteAnimationTargetCompat app, TransformParams params) { 240 builder.withAlpha(mRecentsAlpha.value); 241 } 242 updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder, RemoteAnimationTargetCompat app, TransformParams params)243 private void updateHomeActivityTransformDuringHomeAnim(SurfaceParams.Builder builder, 244 RemoteAnimationTargetCompat app, TransformParams params) { 245 setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value); 246 } 247 248 @NonNull 249 @Override createActivityAnimationToHome()250 public AnimatorPlaybackController createActivityAnimationToHome() { 251 PendingAnimation pa = new PendingAnimation(mDuration); 252 pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCEL); 253 return pa.createPlaybackController(); 254 } 255 updateHomeAlpha()256 private void updateHomeAlpha() { 257 if (mHomeAlphaParams.getTargetSet() != null) { 258 mHomeAlphaParams.applySurfaceParams( 259 mHomeAlphaParams.createSurfaceParams(BuilderProxy.NO_OP)); 260 } 261 } 262 handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets)263 public boolean handleHomeTaskAppeared(RemoteAnimationTargetCompat[] appearedTaskTargets) { 264 RemoteAnimationTargetCompat appearedTaskTarget = appearedTaskTargets[0]; 265 if (appearedTaskTarget.activityType == ACTIVITY_TYPE_HOME) { 266 RemoteAnimationTargets targets = new RemoteAnimationTargets( 267 new RemoteAnimationTargetCompat[] {appearedTaskTarget}, 268 new RemoteAnimationTargetCompat[0], new RemoteAnimationTargetCompat[0], 269 appearedTaskTarget.mode); 270 mHomeAlphaParams.setTargetSet(targets); 271 updateHomeAlpha(); 272 return true; 273 } 274 return false; 275 } 276 277 @Override playAtomicAnimation(float velocity)278 public void playAtomicAnimation(float velocity) { 279 ObjectAnimator alphaAnim = mHomeAlpha.animateToValue(mHomeAlpha.value, 1); 280 alphaAnim.setDuration(mDuration).setInterpolator(ACCEL); 281 alphaAnim.start(); 282 283 if (mRunningOverHome) { 284 // Spring back launcher scale 285 new SpringAnimationBuilder(mContext) 286 .setStartValue(mVerticalShiftForScale.value) 287 .setEndValue(0) 288 .setStartVelocity(-velocity / mTransitionDragLength) 289 .setMinimumVisibleChange(1f / mDp.heightPx) 290 .setDampingRatio(0.6f) 291 .setStiffness(800) 292 .build(mVerticalShiftForScale, AnimatedFloat.VALUE) 293 .start(); 294 } 295 } 296 297 @Override setAnimation(RectFSpringAnim anim)298 public void setAnimation(RectFSpringAnim anim) { 299 mSpringAnim = anim; 300 } 301 onMessageReceived(Message msg)302 private void onMessageReceived(Message msg) { 303 try { 304 Bundle data = msg.getData(); 305 RectF position = data.getParcelable(EXTRA_ICON_POSITION); 306 if (!position.isEmpty()) { 307 mSurfaceControl = data.getParcelable(EXTRA_ICON_SURFACE); 308 mTargetRect.set(position); 309 if (mSpringAnim != null) { 310 mSpringAnim.onTargetPositionChanged(); 311 } 312 } 313 } catch (Exception e) { 314 // Ignore 315 } 316 } 317 318 @Override update(RectF currentRect, float progress, float radius)319 public void update(RectF currentRect, float progress, float radius) { 320 if (mSurfaceControl != null) { 321 currentRect.roundOut(mTempRect); 322 Transaction t = new Transaction(); 323 try { 324 t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0); 325 t.apply(); 326 } catch (RuntimeException e) { 327 // Ignore 328 } 329 } 330 } 331 addGestureContract(Intent intent)332 private void addGestureContract(Intent intent) { 333 if (mRunningOverHome || mGestureState.getRunningTask() == null) { 334 return; 335 } 336 337 TaskKey key = new TaskKey(mGestureState.getRunningTask()); 338 if (key.getComponent() != null) { 339 if (sMessageReceiver == null) { 340 sMessageReceiver = new StaticMessageReceiver(); 341 } 342 343 Bundle gestureNavContract = new Bundle(); 344 gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent()); 345 gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId)); 346 gestureNavContract.putParcelable(EXTRA_REMOTE_CALLBACK, 347 sMessageReceiver.newCallback(this::onMessageReceived)); 348 intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract); 349 } 350 } 351 } 352 353 private static class StaticMessageReceiver implements Handler.Callback { 354 355 private final Messenger mMessenger = 356 new Messenger(new Handler(Looper.getMainLooper(), this)); 357 358 private ParcelUuid mCurrentUID = new ParcelUuid(UUID.randomUUID()); 359 private WeakReference<Consumer<Message>> mCurrentCallback = new WeakReference<>(null); 360 newCallback(Consumer<Message> callback)361 public Message newCallback(Consumer<Message> callback) { 362 mCurrentUID = new ParcelUuid(UUID.randomUUID()); 363 mCurrentCallback = new WeakReference<>(callback); 364 365 Message msg = Message.obtain(); 366 msg.replyTo = mMessenger; 367 msg.obj = mCurrentUID; 368 return msg; 369 } 370 371 @Override handleMessage(@onNull Message message)372 public boolean handleMessage(@NonNull Message message) { 373 if (mCurrentUID.equals(message.obj)) { 374 Consumer<Message> consumer = mCurrentCallback.get(); 375 if (consumer != null) { 376 consumer.accept(message); 377 return true; 378 } 379 } 380 return false; 381 } 382 } 383 } 384