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