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.app.WindowConfiguration.ACTIVITY_TYPE_HOME; 19 import static android.content.Intent.EXTRA_COMPONENT_NAME; 20 import static android.content.Intent.EXTRA_USER; 21 22 import static com.android.app.animation.Interpolators.ACCELERATE; 23 import static com.android.launcher3.GestureNavContract.EXTRA_GESTURE_CONTRACT; 24 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_POSITION; 25 import static com.android.launcher3.GestureNavContract.EXTRA_ICON_SURFACE; 26 import static com.android.launcher3.GestureNavContract.EXTRA_ON_FINISH_CALLBACK; 27 import static com.android.launcher3.GestureNavContract.EXTRA_REMOTE_CALLBACK; 28 import static com.android.launcher3.anim.AnimatorListeners.forEndCallback; 29 import static com.android.quickstep.OverviewComponentObserver.startHomeIntentSafely; 30 31 import android.animation.ObjectAnimator; 32 import android.app.ActivityManager.RunningTaskInfo; 33 import android.app.ActivityOptions; 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.Bundle; 40 import android.os.Handler; 41 import android.os.IBinder; 42 import android.os.Looper; 43 import android.os.Message; 44 import android.os.Messenger; 45 import android.os.ParcelUuid; 46 import android.os.RemoteException; 47 import android.os.UserHandle; 48 import android.util.Log; 49 import android.view.RemoteAnimationTarget; 50 import android.view.Surface; 51 import android.view.SurfaceControl; 52 import android.view.SurfaceControl.Transaction; 53 54 import androidx.annotation.NonNull; 55 import androidx.annotation.Nullable; 56 57 import com.android.launcher3.DeviceProfile; 58 import com.android.launcher3.Utilities; 59 import com.android.launcher3.anim.AnimatedFloat; 60 import com.android.launcher3.anim.AnimatorPlaybackController; 61 import com.android.launcher3.anim.PendingAnimation; 62 import com.android.launcher3.anim.SpringAnimationBuilder; 63 import com.android.launcher3.states.StateAnimationConfig; 64 import com.android.launcher3.util.DisplayController; 65 import com.android.quickstep.fallback.FallbackRecentsView; 66 import com.android.quickstep.fallback.RecentsState; 67 import com.android.quickstep.util.RectFSpringAnim; 68 import com.android.quickstep.util.SurfaceTransaction.SurfaceProperties; 69 import com.android.quickstep.util.TransformParams; 70 import com.android.quickstep.util.TransformParams.BuilderProxy; 71 import com.android.quickstep.views.TaskView; 72 import com.android.systemui.shared.recents.model.Task.TaskKey; 73 import com.android.systemui.shared.system.InputConsumerController; 74 75 import java.lang.ref.WeakReference; 76 import java.util.List; 77 import java.util.UUID; 78 import java.util.function.Consumer; 79 80 /** 81 * Handles the navigation gestures when a 3rd party launcher is the default home activity. 82 */ 83 public class FallbackSwipeHandler extends 84 AbsSwipeUpHandler<RecentsActivity, FallbackRecentsView, RecentsState> { 85 86 private static final String TAG = "FallbackSwipeHandler"; 87 88 /** 89 * Message used for receiving gesture nav contract information. We use a static messenger to 90 * avoid leaking too make binders in case the receiving launcher does not handle the contract 91 * properly. 92 */ 93 private static StaticMessageReceiver sMessageReceiver = null; 94 95 private FallbackHomeAnimationFactory mActiveAnimationFactory; 96 private final boolean mRunningOverHome; 97 98 private final Matrix mTmpMatrix = new Matrix(); 99 private float mMaxLauncherScale = 1; 100 101 private boolean mAppCanEnterPip; 102 FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState, TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs, boolean continuingLastGesture, InputConsumerController inputConsumer)103 public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState, 104 TaskAnimationManager taskAnimationManager, GestureState gestureState, long touchTimeMs, 105 boolean continuingLastGesture, InputConsumerController inputConsumer) { 106 super(context, deviceState, taskAnimationManager, gestureState, touchTimeMs, 107 continuingLastGesture, inputConsumer); 108 109 mRunningOverHome = mGestureState.getRunningTask() != null 110 && mGestureState.getRunningTask().isHomeTask(); 111 if (mRunningOverHome) { 112 runActionOnRemoteHandles(remoteTargetHandle -> 113 remoteTargetHandle.getTransformParams().setHomeBuilderProxy( 114 FallbackSwipeHandler.this::updateHomeActivityTransformDuringSwipeUp)); 115 } 116 } 117 118 @Override initTransitionEndpoints(DeviceProfile dp)119 protected void initTransitionEndpoints(DeviceProfile dp) { 120 super.initTransitionEndpoints(dp); 121 if (mRunningOverHome) { 122 // Full screen scale should be independent of remote target handle 123 mMaxLauncherScale = 1 / mRemoteTargetHandles[0].getTaskViewSimulator() 124 .getFullScreenScale(); 125 } 126 } 127 updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params)128 private void updateHomeActivityTransformDuringSwipeUp(SurfaceProperties builder, 129 RemoteAnimationTarget app, TransformParams params) { 130 setHomeScaleAndAlpha(builder, app, mCurrentShift.value, 131 Utilities.boundToRange(1 - mCurrentShift.value, 0, 1)); 132 } 133 setHomeScaleAndAlpha(SurfaceProperties builder, RemoteAnimationTarget app, float verticalShift, float alpha)134 private void setHomeScaleAndAlpha(SurfaceProperties builder, 135 RemoteAnimationTarget app, float verticalShift, float alpha) { 136 float scale = Utilities.mapRange(verticalShift, 1, mMaxLauncherScale); 137 mTmpMatrix.setScale(scale, scale, 138 app.localBounds.exactCenterX(), app.localBounds.exactCenterY()); 139 builder.setMatrix(mTmpMatrix).setAlpha(alpha); 140 builder.setShow(); 141 } 142 143 @Override createHomeAnimationFactory( List<IBinder> launchCookies, long duration, boolean isTargetTranslucent, boolean appCanEnterPip, RemoteAnimationTarget runningTaskTarget, @Nullable TaskView targetTaskView)144 protected HomeAnimationFactory createHomeAnimationFactory( 145 List<IBinder> launchCookies, 146 long duration, 147 boolean isTargetTranslucent, 148 boolean appCanEnterPip, 149 RemoteAnimationTarget runningTaskTarget, 150 @Nullable TaskView targetTaskView) { 151 mAppCanEnterPip = appCanEnterPip; 152 if (appCanEnterPip) { 153 return new FallbackPipToHomeAnimationFactory(); 154 } 155 mActiveAnimationFactory = new FallbackHomeAnimationFactory(duration); 156 startHomeIntent(mActiveAnimationFactory, runningTaskTarget, "FallbackSwipeHandler-home"); 157 return mActiveAnimationFactory; 158 } 159 startHomeIntent( @ullable FallbackHomeAnimationFactory gestureContractAnimationFactory, @Nullable RemoteAnimationTarget runningTaskTarget, @NonNull String reason)160 private void startHomeIntent( 161 @Nullable FallbackHomeAnimationFactory gestureContractAnimationFactory, 162 @Nullable RemoteAnimationTarget runningTaskTarget, 163 @NonNull String reason) { 164 ActivityOptions options = ActivityOptions.makeCustomAnimation(mContext, 0, 0); 165 Intent intent = new Intent(mGestureState.getHomeIntent()); 166 if (gestureContractAnimationFactory != null && runningTaskTarget != null) { 167 gestureContractAnimationFactory.addGestureContract(intent, runningTaskTarget.taskInfo); 168 } 169 startHomeIntentSafely(mContext, intent, options.toBundle(), reason); 170 } 171 172 @Override handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget)173 protected boolean handleTaskAppeared(RemoteAnimationTarget[] appearedTaskTarget) { 174 if (mActiveAnimationFactory != null 175 && mActiveAnimationFactory.handleHomeTaskAppeared(appearedTaskTarget)) { 176 mActiveAnimationFactory = null; 177 return false; 178 } 179 180 return super.handleTaskAppeared(appearedTaskTarget); 181 } 182 183 @Override finishRecentsControllerToHome(Runnable callback)184 protected void finishRecentsControllerToHome(Runnable callback) { 185 final Runnable recentsCallback; 186 if (mAppCanEnterPip) { 187 // Make sure Launcher is resumed after auto-enter-pip transition to actually trigger 188 // the PiP task appearing. 189 recentsCallback = () -> { 190 callback.run(); 191 startHomeIntent(null /* gestureContractAnimationFactory */, 192 null /* runningTaskTarget */, "FallbackSwipeHandler-resumeLauncher"); 193 }; 194 } else { 195 recentsCallback = callback; 196 } 197 mRecentsView.cleanupRemoteTargets(); 198 mRecentsAnimationController.finish( 199 mAppCanEnterPip /* toRecents */, recentsCallback, true /* sendUserLeaveHint */); 200 } 201 202 @Override switchToScreenshot()203 protected void switchToScreenshot() { 204 if (mRunningOverHome) { 205 // When the current task is home, then we don't need to capture anything 206 mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED); 207 } else { 208 super.switchToScreenshot(); 209 } 210 } 211 212 @Override notifyGestureAnimationStartToRecents()213 protected void notifyGestureAnimationStartToRecents() { 214 if (mRunningOverHome) { 215 if (DisplayController.getNavigationMode(mContext).hasGestures) { 216 mRecentsView.onGestureAnimationStartOnHome( 217 mGestureState.getRunningTask().getPlaceholderTasks(), 218 mDeviceState.getRotationTouchHelper()); 219 } 220 } else { 221 super.notifyGestureAnimationStartToRecents(); 222 } 223 } 224 225 private class FallbackPipToHomeAnimationFactory extends HomeAnimationFactory { 226 @NonNull 227 @Override createActivityAnimationToHome()228 public AnimatorPlaybackController createActivityAnimationToHome() { 229 // copied from {@link LauncherSwipeHandlerV2.LauncherHomeAnimationFactory} 230 long accuracy = 2 * Math.max(mDp.widthPx, mDp.heightPx); 231 return mContainer.getStateManager().createAnimationToNewWorkspace( 232 RecentsState.HOME, accuracy, StateAnimationConfig.SKIP_ALL_ANIMATIONS); 233 } 234 } 235 236 private class FallbackHomeAnimationFactory extends HomeAnimationFactory 237 implements Consumer<Message> { 238 private final Rect mTempRect = new Rect(); 239 private final TransformParams mHomeAlphaParams = new TransformParams(); 240 private final AnimatedFloat mHomeAlpha; 241 242 private final AnimatedFloat mVerticalShiftForScale = new AnimatedFloat(); 243 private final AnimatedFloat mRecentsAlpha = new AnimatedFloat(); 244 245 private final RectF mTargetRect = new RectF(); 246 private SurfaceControl mSurfaceControl; 247 248 private boolean mAnimationFinished; 249 private Message mOnFinishCallback; 250 251 private final long mDuration; 252 253 private RectFSpringAnim mSpringAnim; FallbackHomeAnimationFactory(long duration)254 FallbackHomeAnimationFactory(long duration) { 255 mDuration = duration; 256 257 if (mRunningOverHome) { 258 mHomeAlpha = new AnimatedFloat(); 259 mHomeAlpha.value = Utilities.boundToRange(1 - mCurrentShift.value, 0, 1); 260 mVerticalShiftForScale.value = mCurrentShift.value; 261 runActionOnRemoteHandles(remoteTargetHandle -> 262 remoteTargetHandle.getTransformParams().setHomeBuilderProxy( 263 FallbackHomeAnimationFactory.this 264 ::updateHomeActivityTransformDuringHomeAnim)); 265 } else { 266 mHomeAlpha = new AnimatedFloat(this::updateHomeAlpha); 267 mHomeAlpha.value = 0; 268 mHomeAlphaParams.setHomeBuilderProxy( 269 this::updateHomeActivityTransformDuringHomeAnim); 270 } 271 272 mRecentsAlpha.value = 1; 273 runActionOnRemoteHandles(remoteTargetHandle -> 274 remoteTargetHandle.getTransformParams().setBaseBuilderProxy( 275 FallbackHomeAnimationFactory.this 276 ::updateRecentsActivityTransformDuringHomeAnim)); 277 } 278 279 @NonNull 280 @Override getWindowTargetRect()281 public RectF getWindowTargetRect() { 282 if (mTargetRect.isEmpty()) { 283 mTargetRect.set(super.getWindowTargetRect()); 284 } 285 return mTargetRect; 286 } 287 updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params)288 private void updateRecentsActivityTransformDuringHomeAnim(SurfaceProperties builder, 289 RemoteAnimationTarget app, TransformParams params) { 290 builder.setAlpha(mRecentsAlpha.value); 291 } 292 updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder, RemoteAnimationTarget app, TransformParams params)293 private void updateHomeActivityTransformDuringHomeAnim(SurfaceProperties builder, 294 RemoteAnimationTarget app, TransformParams params) { 295 setHomeScaleAndAlpha(builder, app, mVerticalShiftForScale.value, mHomeAlpha.value); 296 } 297 298 @NonNull 299 @Override createActivityAnimationToHome()300 public AnimatorPlaybackController createActivityAnimationToHome() { 301 PendingAnimation pa = new PendingAnimation(mDuration); 302 pa.setFloat(mRecentsAlpha, AnimatedFloat.VALUE, 0, ACCELERATE); 303 return pa.createPlaybackController(); 304 } 305 updateHomeAlpha()306 private void updateHomeAlpha() { 307 if (mHomeAlphaParams.getTargetSet() != null) { 308 mHomeAlphaParams.applySurfaceParams( 309 mHomeAlphaParams.createSurfaceParams(BuilderProxy.NO_OP)); 310 } 311 } 312 handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets)313 public boolean handleHomeTaskAppeared(RemoteAnimationTarget[] appearedTaskTargets) { 314 RemoteAnimationTarget appearedTaskTarget = appearedTaskTargets[0]; 315 if (appearedTaskTarget.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME) { 316 RemoteAnimationTargets targets = new RemoteAnimationTargets( 317 new RemoteAnimationTarget[] {appearedTaskTarget}, 318 new RemoteAnimationTarget[0], new RemoteAnimationTarget[0], 319 appearedTaskTarget.mode); 320 mHomeAlphaParams.setTargetSet(targets); 321 updateHomeAlpha(); 322 return true; 323 } 324 return false; 325 } 326 327 @Override playAtomicAnimation(float velocity)328 public void playAtomicAnimation(float velocity) { 329 ObjectAnimator alphaAnim = mHomeAlpha.animateToValue(mHomeAlpha.value, 1); 330 alphaAnim.setDuration(mDuration).setInterpolator(ACCELERATE); 331 alphaAnim.start(); 332 333 if (mRunningOverHome) { 334 // Spring back launcher scale 335 new SpringAnimationBuilder(mContext) 336 .setStartValue(mVerticalShiftForScale.value) 337 .setEndValue(0) 338 .setStartVelocity(-velocity / mTransitionDragLength) 339 .setMinimumVisibleChange(1f / mDp.heightPx) 340 .setDampingRatio(0.6f) 341 .setStiffness(800) 342 .build(mVerticalShiftForScale, AnimatedFloat.VALUE) 343 .start(); 344 } 345 } 346 347 @Override setAnimation(RectFSpringAnim anim)348 public void setAnimation(RectFSpringAnim anim) { 349 mSpringAnim = anim; 350 mSpringAnim.addAnimatorListener(forEndCallback(this::onRectAnimationEnd)); 351 } 352 onRectAnimationEnd()353 private void onRectAnimationEnd() { 354 mAnimationFinished = true; 355 maybeSendEndMessage(); 356 } 357 maybeSendEndMessage()358 private void maybeSendEndMessage() { 359 if (mAnimationFinished && mOnFinishCallback != null) { 360 try { 361 mOnFinishCallback.replyTo.send(mOnFinishCallback); 362 } catch (RemoteException e) { 363 Log.e(TAG, "Error sending icon position", e); 364 } 365 } 366 } 367 368 @Override accept(Message msg)369 public void accept(Message msg) { 370 try { 371 Bundle data = msg.getData(); 372 RectF position = data.getParcelable(EXTRA_ICON_POSITION); 373 if (!position.isEmpty()) { 374 mSurfaceControl = data.getParcelable(EXTRA_ICON_SURFACE); 375 mTargetRect.set(position); 376 if (mSpringAnim != null) { 377 mSpringAnim.onTargetPositionChanged(); 378 } 379 } 380 mOnFinishCallback = data.getParcelable(EXTRA_ON_FINISH_CALLBACK); 381 maybeSendEndMessage(); 382 } catch (Exception e) { 383 // Ignore 384 } 385 } 386 387 @Override update(RectF currentRect, float progress, float radius, int overlayAlpha)388 public void update(RectF currentRect, float progress, float radius, int overlayAlpha) { 389 if (mSurfaceControl != null) { 390 currentRect.roundOut(mTempRect); 391 Transaction t = new Transaction(); 392 try { 393 t.setGeometry(mSurfaceControl, null, mTempRect, Surface.ROTATION_0); 394 t.apply(); 395 } catch (RuntimeException e) { 396 // Ignore 397 } 398 } 399 } 400 addGestureContract(Intent intent, RunningTaskInfo runningTaskInfo)401 private void addGestureContract(Intent intent, RunningTaskInfo runningTaskInfo) { 402 if (mRunningOverHome || runningTaskInfo == null) { 403 return; 404 } 405 406 TaskKey key = new TaskKey(runningTaskInfo); 407 if (key.getComponent() != null) { 408 if (sMessageReceiver == null) { 409 sMessageReceiver = new StaticMessageReceiver(); 410 } 411 412 Bundle gestureNavContract = new Bundle(); 413 gestureNavContract.putParcelable(EXTRA_COMPONENT_NAME, key.getComponent()); 414 gestureNavContract.putParcelable(EXTRA_USER, UserHandle.of(key.userId)); 415 gestureNavContract.putParcelable( 416 EXTRA_REMOTE_CALLBACK, sMessageReceiver.newCallback(this)); 417 intent.putExtra(EXTRA_GESTURE_CONTRACT, gestureNavContract); 418 } 419 } 420 } 421 422 private static class StaticMessageReceiver implements Handler.Callback { 423 424 private final Messenger mMessenger = 425 new Messenger(new Handler(Looper.getMainLooper(), this)); 426 427 private ParcelUuid mCurrentUID = new ParcelUuid(UUID.randomUUID()); 428 private WeakReference<Consumer<Message>> mCurrentCallback = new WeakReference<>(null); 429 newCallback(Consumer<Message> callback)430 public Message newCallback(Consumer<Message> callback) { 431 mCurrentUID = new ParcelUuid(UUID.randomUUID()); 432 mCurrentCallback = new WeakReference<>(callback); 433 434 Message msg = Message.obtain(); 435 msg.replyTo = mMessenger; 436 msg.obj = mCurrentUID; 437 return msg; 438 } 439 440 @Override handleMessage(@onNull Message message)441 public boolean handleMessage(@NonNull Message message) { 442 if (mCurrentUID.equals(message.obj)) { 443 Consumer<Message> consumer = mCurrentCallback.get(); 444 if (consumer != null) { 445 consumer.accept(message); 446 return true; 447 } 448 } 449 return false; 450 } 451 } 452 } 453