• 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 
17 package com.android.quickstep;
18 
19 import static android.content.Intent.ACTION_PACKAGE_ADDED;
20 import static android.content.Intent.ACTION_PACKAGE_CHANGED;
21 import static android.content.Intent.ACTION_PACKAGE_REMOVED;
22 
23 import static com.android.launcher3.Utilities.createHomeIntent;
24 import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
25 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
26 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
27 
28 import android.content.BroadcastReceiver;
29 import android.content.ComponentName;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.pm.ActivityInfo;
34 import android.content.pm.PackageManager;
35 import android.content.pm.ResolveInfo;
36 import android.util.SparseIntArray;
37 
38 import com.android.launcher3.tracing.OverviewComponentObserverProto;
39 import com.android.launcher3.tracing.TouchInteractionServiceProto;
40 import com.android.launcher3.util.SimpleBroadcastReceiver;
41 import com.android.systemui.shared.system.PackageManagerWrapper;
42 
43 import java.io.PrintWriter;
44 import java.util.ArrayList;
45 import java.util.Objects;
46 import java.util.function.Consumer;
47 
48 /**
49  * Class to keep track of the current overview component based off user preferences and app updates
50  * and provide callers the relevant classes.
51  */
52 public final class OverviewComponentObserver {
53     private final BroadcastReceiver mUserPreferenceChangeReceiver =
54             new SimpleBroadcastReceiver(this::updateOverviewTargets);
55     private final BroadcastReceiver mOtherHomeAppUpdateReceiver =
56             new SimpleBroadcastReceiver(this::updateOverviewTargets);
57 
58     private final Context mContext;
59     private final RecentsAnimationDeviceState mDeviceState;
60     private final Intent mCurrentHomeIntent;
61     private final Intent mMyHomeIntent;
62     private final Intent mFallbackIntent;
63     private final SparseIntArray mConfigChangesMap = new SparseIntArray();
64 
65     private Consumer<Boolean> mOverviewChangeListener = b -> { };
66 
67     private String mUpdateRegisteredPackage;
68     private BaseActivityInterface mActivityInterface;
69     private Intent mOverviewIntent;
70     private boolean mIsHomeAndOverviewSame;
71     private boolean mIsDefaultHome;
72     private boolean mIsHomeDisabled;
73 
74 
OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState)75     public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
76         mContext = context;
77         mDeviceState = deviceState;
78         mCurrentHomeIntent = createHomeIntent();
79         mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
80         ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
81         ComponentName myHomeComponent =
82                 new ComponentName(context.getPackageName(), info.activityInfo.name);
83         mMyHomeIntent.setComponent(myHomeComponent);
84         mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
85 
86         ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
87         mFallbackIntent = new Intent(Intent.ACTION_MAIN)
88                 .addCategory(Intent.CATEGORY_DEFAULT)
89                 .setComponent(fallbackComponent)
90                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
91 
92         try {
93             ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo(
94                     mFallbackIntent.getComponent(), 0 /* flags */);
95             mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
96         } catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
97 
98         mContext.registerReceiver(mUserPreferenceChangeReceiver,
99                 new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
100         updateOverviewTargets();
101     }
102 
103     /**
104      * Sets a listener for changes in {@link #isHomeAndOverviewSame()}
105      */
setOverviewChangeListener(Consumer<Boolean> overviewChangeListener)106     public void setOverviewChangeListener(Consumer<Boolean> overviewChangeListener) {
107         mOverviewChangeListener = overviewChangeListener;
108     }
109 
onSystemUiStateChanged()110     public void onSystemUiStateChanged() {
111         if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
112             updateOverviewTargets();
113         }
114 
115         // Notify ALL_APPS touch controller when one handed mode state activated or deactivated
116         if (mDeviceState.isOneHandedModeEnabled()) {
117             mActivityInterface.onOneHandedModeStateChanged(mDeviceState.isOneHandedModeActive());
118         }
119     }
120 
updateOverviewTargets(Intent unused)121     private void updateOverviewTargets(Intent unused) {
122         updateOverviewTargets();
123     }
124 
125     /**
126      * Update overview intent and {@link BaseActivityInterface} based off the current launcher home
127      * component.
128      */
updateOverviewTargets()129     private void updateOverviewTargets() {
130         ComponentName defaultHome = PackageManagerWrapper.getInstance()
131                 .getHomeActivities(new ArrayList<>());
132 
133         mIsHomeDisabled = mDeviceState.isHomeDisabled();
134         mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
135 
136         // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
137         // launcher made invisible become visible again before the new activity control helper
138         // becomes active.
139         if (mActivityInterface != null) {
140             mActivityInterface.onAssistantVisibilityChanged(0.f);
141         }
142 
143         if (SEPARATE_RECENTS_ACTIVITY.get()) {
144             mIsDefaultHome = false;
145             if (defaultHome == null) {
146                 defaultHome = mMyHomeIntent.getComponent();
147             }
148         }
149 
150         if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
151             // User default home is same as out home app. Use Overview integrated in Launcher.
152             mActivityInterface = LauncherActivityInterface.INSTANCE;
153             mIsHomeAndOverviewSame = true;
154             mOverviewIntent = mMyHomeIntent;
155             mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
156 
157             // Remove any update listener as we don't care about other packages.
158             unregisterOtherHomeAppUpdateReceiver();
159         } else {
160             // The default home app is a different launcher. Use the fallback Overview instead.
161 
162             mActivityInterface = FallbackActivityInterface.INSTANCE;
163             mIsHomeAndOverviewSame = false;
164             mOverviewIntent = mFallbackIntent;
165             mCurrentHomeIntent.setComponent(defaultHome);
166 
167             // User's default home app can change as a result of package updates of this app (such
168             // as uninstalling the app or removing the "Launcher" feature in an update).
169             // Listen for package updates of this app (and remove any previously attached
170             // package listener).
171             if (defaultHome == null) {
172                 unregisterOtherHomeAppUpdateReceiver();
173             } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
174                 unregisterOtherHomeAppUpdateReceiver();
175 
176                 mUpdateRegisteredPackage = defaultHome.getPackageName();
177                 mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
178                         mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
179                         ACTION_PACKAGE_REMOVED));
180             }
181         }
182         mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
183     }
184 
185     /**
186      * Clean up any registered receivers.
187      */
onDestroy()188     public void onDestroy() {
189         mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
190         unregisterOtherHomeAppUpdateReceiver();
191     }
192 
unregisterOtherHomeAppUpdateReceiver()193     private void unregisterOtherHomeAppUpdateReceiver() {
194         if (mUpdateRegisteredPackage != null) {
195             mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
196             mUpdateRegisteredPackage = null;
197         }
198     }
199 
200     /**
201      * @return {@code true} if the overview component is able to handle the configuration changes.
202      */
canHandleConfigChanges(ComponentName component, int changes)203     boolean canHandleConfigChanges(ComponentName component, int changes) {
204         final int orientationChange =
205                 ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE;
206         if ((changes & orientationChange) == orientationChange) {
207             // This is just an approximate guess for simple orientation change because the changes
208             // may contain non-public bits (e.g. window configuration).
209             return true;
210         }
211 
212         int configMask = mConfigChangesMap.get(component.hashCode());
213         return configMask != 0 && (~configMask & changes) == 0;
214     }
215 
216     /**
217      * Get the intent for overview activity. It is used when lockscreen is shown and home was died
218      * in background, we still want to restart the one that will be used after unlock.
219      *
220      * @return the overview intent
221      */
getOverviewIntentIgnoreSysUiState()222     Intent getOverviewIntentIgnoreSysUiState() {
223         return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
224     }
225 
226     /**
227      * Get the current intent for going to the overview activity.
228      *
229      * @return the overview intent
230      */
getOverviewIntent()231     public Intent getOverviewIntent() {
232         return mOverviewIntent;
233     }
234 
235     /**
236      * Get the current intent for going to the home activity.
237      */
getHomeIntent()238     public Intent getHomeIntent() {
239         return mCurrentHomeIntent;
240     }
241 
242     /**
243      * Returns true if home and overview are same activity.
244      */
isHomeAndOverviewSame()245     public boolean isHomeAndOverviewSame() {
246         return mIsHomeAndOverviewSame;
247     }
248 
249     /**
250      * Get the current activity control helper for managing interactions to the overview activity.
251      *
252      * @return the current activity control helper
253      */
getActivityInterface()254     public BaseActivityInterface getActivityInterface() {
255         return mActivityInterface;
256     }
257 
dump(PrintWriter pw)258     public void dump(PrintWriter pw) {
259         pw.println("OverviewComponentObserver:");
260         pw.println("  isDefaultHome=" + mIsDefaultHome);
261         pw.println("  isHomeDisabled=" + mIsHomeDisabled);
262         pw.println("  homeAndOverviewSame=" + mIsHomeAndOverviewSame);
263         pw.println("  overviewIntent=" + mOverviewIntent);
264         pw.println("  homeIntent=" + mCurrentHomeIntent);
265     }
266 
267     /**
268      * Used for winscope tracing, see launcher_trace.proto
269      * @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
270      * @param serviceProto The parent of this proto message.
271      */
writeToProto(TouchInteractionServiceProto.Builder serviceProto)272     public void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
273         OverviewComponentObserverProto.Builder overviewComponentObserver =
274                 OverviewComponentObserverProto.newBuilder();
275         overviewComponentObserver.setOverviewActivityStarted(mActivityInterface.isStarted());
276         overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
277         serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
278     }
279 }
280