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.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
24 import static com.android.launcher3.util.PackageManagerHelper.getPackageFilter;
25 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
26 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
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.util.SimpleBroadcastReceiver;
39 import com.android.systemui.shared.system.PackageManagerWrapper;
40 
41 import java.io.PrintWriter;
42 import java.util.ArrayList;
43 import java.util.Objects;
44 import java.util.function.Consumer;
45 
46 /**
47  * Class to keep track of the current overview component based off user preferences and app updates
48  * and provide callers the relevant classes.
49  */
50 public final class OverviewComponentObserver {
51     private final BroadcastReceiver mUserPreferenceChangeReceiver =
52             new SimpleBroadcastReceiver(this::updateOverviewTargets);
53     private final BroadcastReceiver mOtherHomeAppUpdateReceiver =
54             new SimpleBroadcastReceiver(this::updateOverviewTargets);
55 
56     private final Context mContext;
57     private final RecentsAnimationDeviceState mDeviceState;
58     private final Intent mCurrentHomeIntent;
59     private final Intent mMyHomeIntent;
60     private final Intent mFallbackIntent;
61     private final SparseIntArray mConfigChangesMap = new SparseIntArray();
62 
63     private Consumer<Boolean> mOverviewChangeListener = b -> { };
64 
65     private String mUpdateRegisteredPackage;
66     private BaseActivityInterface mActivityInterface;
67     private Intent mOverviewIntent;
68     private boolean mIsHomeAndOverviewSame;
69     private boolean mIsDefaultHome;
70     private boolean mIsHomeDisabled;
71 
72 
OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState)73     public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
74         mContext = context;
75         mDeviceState = deviceState;
76         mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
77                 .addCategory(Intent.CATEGORY_HOME)
78                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
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 
updateOverviewTargets(Intent unused)116     private void updateOverviewTargets(Intent unused) {
117         updateOverviewTargets();
118     }
119 
assistantGestureIsConstrained()120     public boolean assistantGestureIsConstrained() {
121         return (mDeviceState.getSystemUiStateFlags() & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
122     }
123 
124     /**
125      * Update overview intent and {@link BaseActivityInterface} based off the current launcher home
126      * component.
127      */
updateOverviewTargets()128     private void updateOverviewTargets() {
129         ComponentName defaultHome = PackageManagerWrapper.getInstance()
130                 .getHomeActivities(new ArrayList<>());
131 
132         mIsHomeDisabled = mDeviceState.isHomeDisabled();
133         mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
134 
135         // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
136         // launcher made invisible become visible again before the new activity control helper
137         // becomes active.
138         if (mActivityInterface != null) {
139             mActivityInterface.onAssistantVisibilityChanged(0.f);
140         }
141 
142         if (SEPARATE_RECENTS_ACTIVITY.get()) {
143             mIsDefaultHome = false;
144             if (defaultHome == null) {
145                 defaultHome = mMyHomeIntent.getComponent();
146             }
147         }
148 
149         if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
150             // User default home is same as out home app. Use Overview integrated in Launcher.
151             mActivityInterface = LauncherActivityInterface.INSTANCE;
152             mIsHomeAndOverviewSame = true;
153             mOverviewIntent = mMyHomeIntent;
154             mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
155 
156             // Remove any update listener as we don't care about other packages.
157             unregisterOtherHomeAppUpdateReceiver();
158         } else {
159             // The default home app is a different launcher. Use the fallback Overview instead.
160 
161             mActivityInterface = FallbackActivityInterface.INSTANCE;
162             mIsHomeAndOverviewSame = false;
163             mOverviewIntent = mFallbackIntent;
164             mCurrentHomeIntent.setComponent(defaultHome);
165 
166             // User's default home app can change as a result of package updates of this app (such
167             // as uninstalling the app or removing the "Launcher" feature in an update).
168             // Listen for package updates of this app (and remove any previously attached
169             // package listener).
170             if (defaultHome == null) {
171                 unregisterOtherHomeAppUpdateReceiver();
172             } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
173                 unregisterOtherHomeAppUpdateReceiver();
174 
175                 mUpdateRegisteredPackage = defaultHome.getPackageName();
176                 mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
177                         mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
178                         ACTION_PACKAGE_REMOVED));
179             }
180         }
181         mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
182     }
183 
184     /**
185      * Clean up any registered receivers.
186      */
onDestroy()187     public void onDestroy() {
188         mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
189         unregisterOtherHomeAppUpdateReceiver();
190     }
191 
unregisterOtherHomeAppUpdateReceiver()192     private void unregisterOtherHomeAppUpdateReceiver() {
193         if (mUpdateRegisteredPackage != null) {
194             mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
195             mUpdateRegisteredPackage = null;
196         }
197     }
198 
199     /**
200      * @return {@code true} if the overview component is able to handle the configuration changes.
201      */
canHandleConfigChanges(ComponentName component, int changes)202     boolean canHandleConfigChanges(ComponentName component, int changes) {
203         final int orientationChange =
204                 ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE;
205         if ((changes & orientationChange) == orientationChange) {
206             // This is just an approximate guess for simple orientation change because the changes
207             // may contain non-public bits (e.g. window configuration).
208             return true;
209         }
210 
211         int configMask = mConfigChangesMap.get(component.hashCode());
212         return configMask != 0 && (~configMask & changes) == 0;
213     }
214 
215     /**
216      * Get the intent for overview activity. It is used when lockscreen is shown and home was died
217      * in background, we still want to restart the one that will be used after unlock.
218      *
219      * @return the overview intent
220      */
getOverviewIntentIgnoreSysUiState()221     Intent getOverviewIntentIgnoreSysUiState() {
222         return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
223     }
224 
225     /**
226      * Get the current intent for going to the overview activity.
227      *
228      * @return the overview intent
229      */
getOverviewIntent()230     public Intent getOverviewIntent() {
231         return mOverviewIntent;
232     }
233 
234     /**
235      * Get the current intent for going to the home activity.
236      */
getHomeIntent()237     public Intent getHomeIntent() {
238         return mCurrentHomeIntent;
239     }
240 
241     /**
242      * Returns true if home and overview are same activity.
243      */
isHomeAndOverviewSame()244     public boolean isHomeAndOverviewSame() {
245         return mIsHomeAndOverviewSame;
246     }
247 
248     /**
249      * Get the current activity control helper for managing interactions to the overview activity.
250      *
251      * @return the current activity control helper
252      */
getActivityInterface()253     public BaseActivityInterface getActivityInterface() {
254         return mActivityInterface;
255     }
256 
dump(PrintWriter pw)257     public void dump(PrintWriter pw) {
258         pw.println("OverviewComponentObserver:");
259         pw.println("  isDefaultHome=" + mIsDefaultHome);
260         pw.println("  isHomeDisabled=" + mIsHomeDisabled);
261         pw.println("  homeAndOverviewSame=" + mIsHomeAndOverviewSame);
262         pw.println("  overviewIntent=" + mOverviewIntent);
263         pw.println("  homeIntent=" + mCurrentHomeIntent);
264     }
265 }
266