• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.ACTION_PACKAGE_ADDED;
19 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
20 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
21 
22 import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
23 import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
24 import static com.android.quickstep.TouchConsumer.INTERACTION_NORMAL;
25 import static com.android.systemui.shared.system.ActivityManagerWrapper
26         .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
27 import static com.android.systemui.shared.system.PackageManagerWrapper
28         .ACTION_PREFERRED_ACTIVITY_CHANGED;
29 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
30 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
31 
32 import android.animation.Animator;
33 import android.animation.AnimatorSet;
34 import android.animation.ValueAnimator;
35 import android.annotation.TargetApi;
36 import android.content.BroadcastReceiver;
37 import android.content.ComponentName;
38 import android.content.Context;
39 import android.content.Intent;
40 import android.content.IntentFilter;
41 import android.content.pm.ResolveInfo;
42 import android.graphics.Rect;
43 import android.os.Build;
44 import android.os.PatternMatcher;
45 import android.os.SystemClock;
46 import android.util.Log;
47 import android.view.View;
48 import android.view.ViewConfiguration;
49 
50 import com.android.launcher3.AbstractFloatingView;
51 import com.android.launcher3.BaseDraggingActivity;
52 import com.android.launcher3.InvariantDeviceProfile;
53 import com.android.launcher3.MainThreadExecutor;
54 import com.android.launcher3.anim.AnimationSuccessListener;
55 import com.android.launcher3.logging.UserEventDispatcher;
56 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
57 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
58 import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
59 import com.android.quickstep.ActivityControlHelper.AnimationFactory;
60 import com.android.quickstep.ActivityControlHelper.FallbackActivityControllerHelper;
61 import com.android.quickstep.ActivityControlHelper.LauncherActivityControllerHelper;
62 import com.android.quickstep.util.ClipAnimationHelper;
63 import com.android.quickstep.util.TransformedRect;
64 import com.android.quickstep.util.RemoteAnimationTargetSet;
65 import com.android.quickstep.views.RecentsView;
66 import com.android.systemui.shared.system.ActivityManagerWrapper;
67 import com.android.systemui.shared.system.LatencyTrackerCompat;
68 import com.android.systemui.shared.system.PackageManagerWrapper;
69 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
70 import com.android.systemui.shared.system.TransactionCompat;
71 
72 import java.util.ArrayList;
73 
74 /**
75  * Helper class to handle various atomic commands for switching between Overview.
76  */
77 @TargetApi(Build.VERSION_CODES.P)
78 public class OverviewCommandHelper {
79 
80     private static final long RECENTS_LAUNCH_DURATION = 250;
81 
82     private static final String TAG = "OverviewCommandHelper";
83 
84     private final Context mContext;
85     private final ActivityManagerWrapper mAM;
86     private final RecentsModel mRecentsModel;
87     private final MainThreadExecutor mMainThreadExecutor;
88     private final ComponentName mMyHomeComponent;
89 
90     private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
91         @Override
92         public void onReceive(Context context, Intent intent) {
93             initOverviewTargets();
94         }
95     };
96     private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
97         @Override
98         public void onReceive(Context context, Intent intent) {
99             initOverviewTargets();
100         }
101     };
102     private String mUpdateRegisteredPackage;
103 
104     public Intent overviewIntent;
105     public ComponentName overviewComponent;
106     private ActivityControlHelper mActivityControlHelper;
107 
108     private long mLastToggleTime;
109 
OverviewCommandHelper(Context context)110     public OverviewCommandHelper(Context context) {
111         mContext = context;
112         mAM = ActivityManagerWrapper.getInstance();
113         mMainThreadExecutor = new MainThreadExecutor();
114         mRecentsModel = RecentsModel.getInstance(mContext);
115 
116         Intent myHomeIntent = new Intent(Intent.ACTION_MAIN)
117                 .addCategory(Intent.CATEGORY_HOME)
118                 .setPackage(mContext.getPackageName());
119         ResolveInfo info = context.getPackageManager().resolveActivity(myHomeIntent, 0);
120         mMyHomeComponent = new ComponentName(context.getPackageName(), info.activityInfo.name);
121 
122         mContext.registerReceiver(mUserPreferenceChangeReceiver,
123                 new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
124         initOverviewTargets();
125     }
126 
initOverviewTargets()127     private void initOverviewTargets() {
128         ComponentName defaultHome = PackageManagerWrapper.getInstance()
129                 .getHomeActivities(new ArrayList<>());
130 
131         final String overviewIntentCategory;
132         if (defaultHome == null || mMyHomeComponent.equals(defaultHome)) {
133             // User default home is same as out home app. Use Overview integrated in Launcher.
134             overviewComponent = mMyHomeComponent;
135             mActivityControlHelper = new LauncherActivityControllerHelper();
136             overviewIntentCategory = Intent.CATEGORY_HOME;
137 
138             if (mUpdateRegisteredPackage != null) {
139                 // Remove any update listener as we don't care about other packages.
140                 mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
141                 mUpdateRegisteredPackage = null;
142             }
143         } else {
144             // The default home app is a different launcher. Use the fallback Overview instead.
145             overviewComponent = new ComponentName(mContext, RecentsActivity.class);
146             mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
147             overviewIntentCategory = Intent.CATEGORY_DEFAULT;
148 
149             // User's default home app can change as a result of package updates of this app (such
150             // as uninstalling the app or removing the "Launcher" feature in an update).
151             // Listen for package updates of this app (and remove any previously attached
152             // package listener).
153             if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
154                 if (mUpdateRegisteredPackage != null) {
155                     mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
156                 }
157 
158                 mUpdateRegisteredPackage = defaultHome.getPackageName();
159                 IntentFilter updateReceiver = new IntentFilter(ACTION_PACKAGE_ADDED);
160                 updateReceiver.addAction(ACTION_PACKAGE_CHANGED);
161                 updateReceiver.addAction(ACTION_PACKAGE_REMOVED);
162                 updateReceiver.addDataScheme("package");
163                 updateReceiver.addDataSchemeSpecificPart(mUpdateRegisteredPackage,
164                         PatternMatcher.PATTERN_LITERAL);
165                 mContext.registerReceiver(mOtherHomeAppUpdateReceiver, updateReceiver);
166             }
167         }
168 
169         overviewIntent = new Intent(Intent.ACTION_MAIN)
170                 .addCategory(overviewIntentCategory)
171                 .setComponent(overviewComponent)
172                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
173     }
174 
onDestroy()175     public void onDestroy() {
176         mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
177 
178         if (mUpdateRegisteredPackage != null) {
179             mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
180             mUpdateRegisteredPackage = null;
181         }
182     }
183 
onOverviewToggle()184     public void onOverviewToggle() {
185         // If currently screen pinning, do not enter overview
186         if (mAM.isScreenPinningActive()) {
187             return;
188         }
189 
190         mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
191         mMainThreadExecutor.execute(new RecentsActivityCommand<>());
192     }
193 
onOverviewShown()194     public void onOverviewShown() {
195         mMainThreadExecutor.execute(new ShowRecentsCommand());
196     }
197 
onTip(int actionType, int viewType)198     public void onTip(int actionType, int viewType) {
199         mMainThreadExecutor.execute(new Runnable() {
200             @Override
201             public void run() {
202                 UserEventDispatcher.newInstance(mContext,
203                         new InvariantDeviceProfile(mContext).getDeviceProfile(mContext))
204                         .logActionTip(actionType, viewType);
205             }
206         });
207     }
208 
getActivityControlHelper()209     public ActivityControlHelper getActivityControlHelper() {
210         return mActivityControlHelper;
211     }
212 
213     private class ShowRecentsCommand extends RecentsActivityCommand {
214 
215         @Override
handleCommand(long elapsedTime)216         protected boolean handleCommand(long elapsedTime) {
217             return mHelper.getVisibleRecentsView() != null;
218         }
219     }
220 
221     private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
222 
223         protected final ActivityControlHelper<T> mHelper;
224         private final long mCreateTime;
225         private final int mRunningTaskId;
226 
227         private ActivityInitListener mListener;
228         private T mActivity;
229         private RecentsView mRecentsView;
230         private final long mToggleClickedTime = SystemClock.uptimeMillis();
231         private boolean mUserEventLogged;
232 
RecentsActivityCommand()233         public RecentsActivityCommand() {
234             mHelper = getActivityControlHelper();
235             mCreateTime = SystemClock.elapsedRealtime();
236             mRunningTaskId = mAM.getRunningTask().id;
237 
238             // Preload the plan
239             mRecentsModel.loadTasks(mRunningTaskId, null);
240         }
241 
242         @Override
run()243         public void run() {
244             long elapsedTime = mCreateTime - mLastToggleTime;
245             mLastToggleTime = mCreateTime;
246 
247             if (!handleCommand(elapsedTime)) {
248                 // Start overview
249                 if (!mHelper.switchToRecentsIfVisible(true)) {
250                     mListener = mHelper.createActivityInitListener(this::onActivityReady);
251                     mListener.registerAndStartActivity(overviewIntent, this::createWindowAnimation,
252                             mContext, mMainThreadExecutor.getHandler(), RECENTS_LAUNCH_DURATION);
253                 }
254             }
255         }
256 
handleCommand(long elapsedTime)257         protected boolean handleCommand(long elapsedTime) {
258             // TODO: We need to fix this case with PIP, when an activity first enters PIP, it shows
259             //       the menu activity which takes window focus, preventing the right condition from
260             //       being run below
261             RecentsView recents = mHelper.getVisibleRecentsView();
262             if (recents != null) {
263                 // Launch the next task
264                 recents.showNextTask();
265                 return true;
266             } else if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
267                 // The user tried to launch back into overview too quickly, either after
268                 // launching an app, or before overview has actually shown, just ignore for now
269                 return true;
270             }
271             return false;
272         }
273 
onActivityReady(T activity, Boolean wasVisible)274         private boolean onActivityReady(T activity, Boolean wasVisible) {
275             activity.<RecentsView>getOverviewPanel().setCurrentTask(mRunningTaskId);
276             AbstractFloatingView.closeAllOpenViews(activity, wasVisible);
277             AnimationFactory factory = mHelper.prepareRecentsUI(activity, wasVisible,
278                     (controller) -> {
279                         controller.dispatchOnStart();
280                         ValueAnimator anim = controller.getAnimationPlayer()
281                                 .setDuration(RECENTS_LAUNCH_DURATION);
282                         anim.setInterpolator(FAST_OUT_SLOW_IN);
283                         anim.start();
284                 });
285             factory.onRemoteAnimationReceived(null);
286             if (wasVisible) {
287                 factory.createActivityController(RECENTS_LAUNCH_DURATION, INTERACTION_NORMAL);
288             }
289             mActivity = activity;
290             mRecentsView = mActivity.getOverviewPanel();
291             mRecentsView.setRunningTaskIconScaledDown(true /* isScaledDown */, false /* animate */);
292             if (!mUserEventLogged) {
293                 activity.getUserEventDispatcher().logActionCommand(Action.Command.RECENTS_BUTTON,
294                         mHelper.getContainerType(), ContainerType.TASKSWITCHER);
295                 mUserEventLogged = true;
296             }
297             return false;
298         }
299 
createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats)300         private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
301             if (LatencyTrackerCompat.isEnabled(mContext)) {
302                 LatencyTrackerCompat.logToggleRecents(
303                         (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
304             }
305 
306             if (mListener != null) {
307                 mListener.unregister();
308             }
309             AnimatorSet anim = new AnimatorSet();
310             anim.addListener(new AnimationSuccessListener() {
311                 @Override
312                 public void onAnimationSuccess(Animator animator) {
313                     if (mRecentsView != null) {
314                         mRecentsView.setRunningTaskIconScaledDown(false /* isScaledDown */,
315                                 true /* animate */);
316                     }
317                 }
318             });
319             if (mActivity == null) {
320                 Log.e(TAG, "Animation created, before activity");
321                 anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
322                 return anim;
323             }
324 
325             RemoteAnimationTargetSet targetSet =
326                     new RemoteAnimationTargetSet(targetCompats, MODE_CLOSING);
327 
328             // Use the top closing app to determine the insets for the animation
329             RemoteAnimationTargetCompat runningTaskTarget = targetSet.findTask(mRunningTaskId);
330             if (runningTaskTarget == null) {
331                 Log.e(TAG, "No closing app");
332                 anim.play(ValueAnimator.ofInt(0, 1).setDuration(100));
333                 return anim;
334             }
335 
336             final ClipAnimationHelper clipHelper = new ClipAnimationHelper();
337 
338             // At this point, the activity is already started and laid-out. Get the home-bounds
339             // relative to the screen using the rootView of the activity.
340             int loc[] = new int[2];
341             View rootView = mActivity.getRootView();
342             rootView.getLocationOnScreen(loc);
343             Rect homeBounds = new Rect(loc[0], loc[1],
344                     loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
345             clipHelper.updateSource(homeBounds, runningTaskTarget);
346 
347             TransformedRect targetRect = new TransformedRect();
348             mHelper.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
349                     INTERACTION_NORMAL, targetRect);
350             clipHelper.updateTargetRect(targetRect);
351             clipHelper.prepareAnimation(false /* isOpening */);
352 
353             ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
354             valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
355             valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
356             valueAnimator.addUpdateListener((v) ->
357                     clipHelper.applyTransform(targetSet, (float) v.getAnimatedValue()));
358 
359             if (targetSet.isAnimatingHome()) {
360                 // If we are animating home, fade in the opening targets
361                 RemoteAnimationTargetSet openingSet =
362                         new RemoteAnimationTargetSet(targetCompats, MODE_OPENING);
363 
364                 TransactionCompat transaction = new TransactionCompat();
365                 valueAnimator.addUpdateListener((v) -> {
366                     for (RemoteAnimationTargetCompat app : openingSet.apps) {
367                         transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
368                     }
369                     transaction.apply();
370                 });
371             }
372             anim.play(valueAnimator);
373             return anim;
374         }
375     }
376 }
377