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