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.util.PackageManagerHelper.getPackageFilter;
24 import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
25 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED;
26 import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
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.systemui.shared.system.PackageManagerWrapper;
39 
40 import java.util.ArrayList;
41 import java.util.Objects;
42 
43 /**
44  * Class to keep track of the current overview component based off user preferences and app updates
45  * and provide callers the relevant classes.
46  */
47 public final class OverviewComponentObserver {
48     private final BroadcastReceiver mUserPreferenceChangeReceiver = new BroadcastReceiver() {
49         @Override
50         public void onReceive(Context context, Intent intent) {
51             updateOverviewTargets();
52         }
53     };
54     private final BroadcastReceiver mOtherHomeAppUpdateReceiver = new BroadcastReceiver() {
55         @Override
56         public void onReceive(Context context, Intent intent) {
57             updateOverviewTargets();
58         }
59     };
60     private final Context mContext;
61     private final Intent mCurrentHomeIntent;
62     private final Intent mMyHomeIntent;
63     private final Intent mFallbackIntent;
64     private final SparseIntArray mConfigChangesMap = new SparseIntArray();
65     private String mUpdateRegisteredPackage;
66     private ActivityControlHelper mActivityControlHelper;
67     private Intent mOverviewIntent;
68     private int mSystemUiStateFlags;
69     private boolean mIsHomeAndOverviewSame;
70     private boolean mIsDefaultHome;
71 
OverviewComponentObserver(Context context)72     public OverviewComponentObserver(Context context) {
73         mContext = context;
74 
75         mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
76                 .addCategory(Intent.CATEGORY_HOME)
77                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
78         mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName());
79         ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0);
80         ComponentName myHomeComponent =
81                 new ComponentName(context.getPackageName(), info.activityInfo.name);
82         mMyHomeIntent.setComponent(myHomeComponent);
83         mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges);
84 
85         ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class);
86         mFallbackIntent = new Intent(Intent.ACTION_MAIN)
87                 .addCategory(Intent.CATEGORY_DEFAULT)
88                 .setComponent(fallbackComponent)
89                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
90 
91         try {
92             ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo(
93                     mFallbackIntent.getComponent(), 0 /* flags */);
94             mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges);
95         } catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
96 
97         mContext.registerReceiver(mUserPreferenceChangeReceiver,
98                 new IntentFilter(ACTION_PREFERRED_ACTIVITY_CHANGED));
99         updateOverviewTargets();
100     }
101 
onSystemUiStateChanged(int stateFlags)102     public void onSystemUiStateChanged(int stateFlags) {
103         boolean homeDisabledChanged = (mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED)
104                 != (stateFlags & SYSUI_STATE_HOME_DISABLED);
105         mSystemUiStateFlags = stateFlags;
106         if (homeDisabledChanged) {
107             updateOverviewTargets();
108         }
109     }
110 
assistantGestureIsConstrained()111     public boolean assistantGestureIsConstrained() {
112         return (mSystemUiStateFlags & SYSUI_STATE_ASSIST_GESTURE_CONSTRAINED) != 0;
113     }
114 
115     /**
116      * Update overview intent and {@link ActivityControlHelper} based off the current launcher home
117      * component.
118      */
updateOverviewTargets()119     private void updateOverviewTargets() {
120         ComponentName defaultHome = PackageManagerWrapper.getInstance()
121                 .getHomeActivities(new ArrayList<>());
122 
123         mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome);
124 
125         // Set assistant visibility to 0 from launcher's perspective, ensures any elements that
126         // launcher made invisible become visible again before the new activity control helper
127         // becomes active.
128         if (mActivityControlHelper != null) {
129             mActivityControlHelper.onAssistantVisibilityChanged(0.f);
130         }
131 
132         if ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
133                 && (defaultHome == null || mIsDefaultHome)) {
134             // User default home is same as out home app. Use Overview integrated in Launcher.
135             mActivityControlHelper = new LauncherActivityControllerHelper();
136             mIsHomeAndOverviewSame = true;
137             mOverviewIntent = mMyHomeIntent;
138             mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
139 
140             if (mUpdateRegisteredPackage != null) {
141                 // Remove any update listener as we don't care about other packages.
142                 mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
143                 mUpdateRegisteredPackage = null;
144             }
145         } else {
146             // The default home app is a different launcher. Use the fallback Overview instead.
147 
148             mActivityControlHelper = new FallbackActivityControllerHelper();
149             mIsHomeAndOverviewSame = false;
150             mOverviewIntent = mFallbackIntent;
151             mCurrentHomeIntent.setComponent(defaultHome);
152 
153             // User's default home app can change as a result of package updates of this app (such
154             // as uninstalling the app or removing the "Launcher" feature in an update).
155             // Listen for package updates of this app (and remove any previously attached
156             // package listener).
157             if (defaultHome == null) {
158                 if (mUpdateRegisteredPackage != null) {
159                     mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
160                 }
161             } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) {
162                 if (mUpdateRegisteredPackage != null) {
163                     mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
164                 }
165 
166                 mUpdateRegisteredPackage = defaultHome.getPackageName();
167                 mContext.registerReceiver(mOtherHomeAppUpdateReceiver, getPackageFilter(
168                         mUpdateRegisteredPackage, ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED,
169                         ACTION_PACKAGE_REMOVED));
170             }
171         }
172     }
173 
174     /**
175      * Clean up any registered receivers.
176      */
onDestroy()177     public void onDestroy() {
178         mContext.unregisterReceiver(mUserPreferenceChangeReceiver);
179 
180         if (mUpdateRegisteredPackage != null) {
181             mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver);
182             mUpdateRegisteredPackage = null;
183         }
184     }
185 
186     /**
187      * @return {@code true} if the overview component is able to handle the configuration changes.
188      */
canHandleConfigChanges(ComponentName component, int changes)189     boolean canHandleConfigChanges(ComponentName component, int changes) {
190         final int orientationChange =
191                 ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE;
192         if ((changes & orientationChange) == orientationChange) {
193             // This is just an approximate guess for simple orientation change because the changes
194             // may contain non-public bits (e.g. window configuration).
195             return true;
196         }
197 
198         int configMask = mConfigChangesMap.get(component.hashCode());
199         return configMask != 0 && (~configMask & changes) == 0;
200     }
201 
202     /**
203      * Get the intent for overview activity. It is used when lockscreen is shown and home was died
204      * in background, we still want to restart the one that will be used after unlock.
205      *
206      * @return the overview intent
207      */
getOverviewIntentIgnoreSysUiState()208     Intent getOverviewIntentIgnoreSysUiState() {
209         return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent;
210     }
211 
212     /**
213      * Get the current intent for going to the overview activity.
214      *
215      * @return the overview intent
216      */
getOverviewIntent()217     public Intent getOverviewIntent() {
218         return mOverviewIntent;
219     }
220 
221     /**
222      * Get the current intent for going to the home activity.
223      */
getHomeIntent()224     public Intent getHomeIntent() {
225         return mCurrentHomeIntent;
226     }
227 
228     /**
229      * Returns true if home and overview are same activity.
230      */
isHomeAndOverviewSame()231     public boolean isHomeAndOverviewSame() {
232         return mIsHomeAndOverviewSame;
233     }
234 
235     /**
236      * Get the current activity control helper for managing interactions to the overview activity.
237      *
238      * @return the current activity control helper
239      */
getActivityControlHelper()240     public ActivityControlHelper getActivityControlHelper() {
241         return mActivityControlHelper;
242     }
243 }
244