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.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED; 25 26 import android.content.ActivityNotFoundException; 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.ActivityInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.os.Bundle; 34 import android.util.Log; 35 import android.util.SparseIntArray; 36 37 import androidx.annotation.NonNull; 38 import androidx.annotation.Nullable; 39 40 import com.android.launcher3.R; 41 import com.android.launcher3.util.SimpleBroadcastReceiver; 42 import com.android.systemui.shared.system.PackageManagerWrapper; 43 44 import java.io.PrintWriter; 45 import java.util.ArrayList; 46 import java.util.Objects; 47 import java.util.function.Consumer; 48 49 /** 50 * Class to keep track of the current overview component based off user preferences and app updates 51 * and provide callers the relevant classes. 52 */ 53 public final class OverviewComponentObserver { 54 private static final String TAG = "OverviewComponentObserver"; 55 56 private final SimpleBroadcastReceiver mUserPreferenceChangeReceiver = 57 new SimpleBroadcastReceiver(this::updateOverviewTargets); 58 private final SimpleBroadcastReceiver mOtherHomeAppUpdateReceiver = 59 new SimpleBroadcastReceiver(this::updateOverviewTargets); 60 61 private final Context mContext; 62 private final RecentsAnimationDeviceState mDeviceState; 63 private final Intent mCurrentHomeIntent; 64 private final Intent mMyHomeIntent; 65 private final Intent mFallbackIntent; 66 private final SparseIntArray mConfigChangesMap = new SparseIntArray(); 67 private final String mSetupWizardPkg; 68 69 private Consumer<Boolean> mOverviewChangeListener = b -> { }; 70 71 private String mUpdateRegisteredPackage; 72 private BaseActivityInterface mActivityInterface; 73 private Intent mOverviewIntent; 74 private boolean mIsHomeAndOverviewSame; 75 private boolean mIsDefaultHome; 76 private boolean mIsHomeDisabled; 77 78 OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState)79 public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) { 80 mContext = context; 81 mDeviceState = deviceState; 82 mCurrentHomeIntent = createHomeIntent(); 83 mMyHomeIntent = new Intent(mCurrentHomeIntent).setPackage(mContext.getPackageName()); 84 ResolveInfo info = context.getPackageManager().resolveActivity(mMyHomeIntent, 0); 85 ComponentName myHomeComponent = 86 new ComponentName(context.getPackageName(), info.activityInfo.name); 87 mMyHomeIntent.setComponent(myHomeComponent); 88 mConfigChangesMap.append(myHomeComponent.hashCode(), info.activityInfo.configChanges); 89 mSetupWizardPkg = context.getString(R.string.setup_wizard_pkg); 90 91 ComponentName fallbackComponent = new ComponentName(mContext, RecentsActivity.class); 92 mFallbackIntent = new Intent(Intent.ACTION_MAIN) 93 .addCategory(Intent.CATEGORY_DEFAULT) 94 .setComponent(fallbackComponent) 95 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 96 97 try { 98 ActivityInfo fallbackInfo = context.getPackageManager().getActivityInfo( 99 mFallbackIntent.getComponent(), 0 /* flags */); 100 mConfigChangesMap.append(fallbackComponent.hashCode(), fallbackInfo.configChanges); 101 } catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ } 102 103 mUserPreferenceChangeReceiver.register(mContext, ACTION_PREFERRED_ACTIVITY_CHANGED); 104 updateOverviewTargets(); 105 } 106 107 /** 108 * Sets a listener for changes in {@link #isHomeAndOverviewSame()} 109 */ setOverviewChangeListener(Consumer<Boolean> overviewChangeListener)110 public void setOverviewChangeListener(Consumer<Boolean> overviewChangeListener) { 111 mOverviewChangeListener = overviewChangeListener; 112 } 113 onSystemUiStateChanged()114 public void onSystemUiStateChanged() { 115 if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) { 116 updateOverviewTargets(); 117 } 118 } 119 updateOverviewTargets(Intent unused)120 private void updateOverviewTargets(Intent unused) { 121 updateOverviewTargets(); 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 if (defaultHome != null && defaultHome.getPackageName().equals(mSetupWizardPkg)) { 132 // Treat setup wizard as null default home, because there is a period between setup and 133 // launcher being default home where it is briefly null. Otherwise, it would appear as 134 // if overview targets are changing twice, giving the listener an incorrect signal. 135 defaultHome = null; 136 } 137 138 mIsHomeDisabled = mDeviceState.isHomeDisabled(); 139 mIsDefaultHome = Objects.equals(mMyHomeIntent.getComponent(), defaultHome); 140 141 // Set assistant visibility to 0 from launcher's perspective, ensures any elements that 142 // launcher made invisible become visible again before the new activity control helper 143 // becomes active. 144 if (mActivityInterface != null) { 145 mActivityInterface.onAssistantVisibilityChanged(0.f); 146 } 147 148 if (SEPARATE_RECENTS_ACTIVITY.get()) { 149 mIsDefaultHome = false; 150 if (defaultHome == null) { 151 defaultHome = mMyHomeIntent.getComponent(); 152 } 153 } 154 155 // TODO(b/258022658): Remove temporary logging. 156 Log.i(TAG, "updateOverviewTargets: mIsHomeDisabled=" + mIsHomeDisabled 157 + ", isDefaultHomeNull=" + (defaultHome == null) 158 + ", mIsDefaultHome=" + mIsDefaultHome); 159 160 if (!mIsHomeDisabled && (defaultHome == null || mIsDefaultHome)) { 161 // User default home is same as out home app. Use Overview integrated in Launcher. 162 mActivityInterface = LauncherActivityInterface.INSTANCE; 163 mIsHomeAndOverviewSame = true; 164 mOverviewIntent = mMyHomeIntent; 165 mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent()); 166 167 // Remove any update listener as we don't care about other packages. 168 unregisterOtherHomeAppUpdateReceiver(); 169 } else { 170 // The default home app is a different launcher. Use the fallback Overview instead. 171 172 mActivityInterface = FallbackActivityInterface.INSTANCE; 173 mIsHomeAndOverviewSame = false; 174 mOverviewIntent = mFallbackIntent; 175 mCurrentHomeIntent.setComponent(defaultHome); 176 177 // User's default home app can change as a result of package updates of this app (such 178 // as uninstalling the app or removing the "Launcher" feature in an update). 179 // Listen for package updates of this app (and remove any previously attached 180 // package listener). 181 if (defaultHome == null) { 182 unregisterOtherHomeAppUpdateReceiver(); 183 } else if (!defaultHome.getPackageName().equals(mUpdateRegisteredPackage)) { 184 unregisterOtherHomeAppUpdateReceiver(); 185 186 mUpdateRegisteredPackage = defaultHome.getPackageName(); 187 mOtherHomeAppUpdateReceiver.registerPkgActions(mContext, mUpdateRegisteredPackage, 188 ACTION_PACKAGE_ADDED, ACTION_PACKAGE_CHANGED, ACTION_PACKAGE_REMOVED); 189 } 190 } 191 mOverviewChangeListener.accept(mIsHomeAndOverviewSame); 192 } 193 194 /** 195 * Clean up any registered receivers. 196 */ onDestroy()197 public void onDestroy() { 198 mContext.unregisterReceiver(mUserPreferenceChangeReceiver); 199 unregisterOtherHomeAppUpdateReceiver(); 200 } 201 unregisterOtherHomeAppUpdateReceiver()202 private void unregisterOtherHomeAppUpdateReceiver() { 203 if (mUpdateRegisteredPackage != null) { 204 mContext.unregisterReceiver(mOtherHomeAppUpdateReceiver); 205 mUpdateRegisteredPackage = null; 206 } 207 } 208 209 /** 210 * @return {@code true} if the overview component is able to handle the configuration changes. 211 */ canHandleConfigChanges(ComponentName component, int changes)212 boolean canHandleConfigChanges(ComponentName component, int changes) { 213 final int orientationChange = 214 ActivityInfo.CONFIG_ORIENTATION | ActivityInfo.CONFIG_SCREEN_SIZE; 215 if ((changes & orientationChange) == orientationChange) { 216 // This is just an approximate guess for simple orientation change because the changes 217 // may contain non-public bits (e.g. window configuration). 218 return true; 219 } 220 221 int configMask = mConfigChangesMap.get(component.hashCode()); 222 return configMask != 0 && (~configMask & changes) == 0; 223 } 224 225 /** 226 * Get the intent for overview activity. It is used when lockscreen is shown and home was died 227 * in background, we still want to restart the one that will be used after unlock. 228 * 229 * @return the overview intent 230 */ getOverviewIntentIgnoreSysUiState()231 Intent getOverviewIntentIgnoreSysUiState() { 232 return mIsDefaultHome ? mMyHomeIntent : mOverviewIntent; 233 } 234 235 /** 236 * Get the current intent for going to the overview activity. 237 * 238 * @return the overview intent 239 */ getOverviewIntent()240 public Intent getOverviewIntent() { 241 return mOverviewIntent; 242 } 243 244 /** 245 * Get the current intent for going to the home activity. 246 */ getHomeIntent()247 public Intent getHomeIntent() { 248 return mCurrentHomeIntent; 249 } 250 251 /** 252 * Returns true if home and overview are same activity. 253 */ isHomeAndOverviewSame()254 public boolean isHomeAndOverviewSame() { 255 return mIsHomeAndOverviewSame; 256 } 257 258 /** 259 * Get the current activity control helper for managing interactions to the overview activity. 260 * 261 * @return the current activity control helper 262 */ getActivityInterface()263 public BaseActivityInterface getActivityInterface() { 264 return mActivityInterface; 265 } 266 dump(PrintWriter pw)267 public void dump(PrintWriter pw) { 268 pw.println("OverviewComponentObserver:"); 269 pw.println(" isDefaultHome=" + mIsDefaultHome); 270 pw.println(" isHomeDisabled=" + mIsHomeDisabled); 271 pw.println(" homeAndOverviewSame=" + mIsHomeAndOverviewSame); 272 pw.println(" overviewIntent=" + mOverviewIntent); 273 pw.println(" homeIntent=" + mCurrentHomeIntent); 274 } 275 276 /** 277 * Starts the intent for the current home activity. 278 */ startHomeIntentSafely(@onNull Context context, @Nullable Bundle options)279 public static void startHomeIntentSafely(@NonNull Context context, @Nullable Bundle options) { 280 RecentsAnimationDeviceState deviceState = new RecentsAnimationDeviceState(context); 281 OverviewComponentObserver observer = new OverviewComponentObserver(context, deviceState); 282 Intent intent = observer.getHomeIntent(); 283 observer.onDestroy(); 284 deviceState.destroy(); 285 startHomeIntentSafely(context, intent, options); 286 } 287 288 /** 289 * Starts the intent for the current home activity. 290 */ startHomeIntentSafely( @onNull Context context, @NonNull Intent homeIntent, @Nullable Bundle options)291 public static void startHomeIntentSafely( 292 @NonNull Context context, @NonNull Intent homeIntent, @Nullable Bundle options) { 293 try { 294 context.startActivity(homeIntent, options); 295 } catch (NullPointerException | ActivityNotFoundException | SecurityException e) { 296 context.startActivity(createHomeIntent(), options); 297 } 298 } 299 createHomeIntent()300 private static Intent createHomeIntent() { 301 return new Intent(Intent.ACTION_MAIN) 302 .addCategory(Intent.CATEGORY_HOME) 303 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 304 } 305 } 306