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