• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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