1 /* 2 * Copyright (C) 2013 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.launcher3; 18 19 import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_RESOURCE_UPDATED; 20 import static android.content.Context.RECEIVER_EXPORTED; 21 22 import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle; 23 import static com.android.launcher3.LauncherPrefs.ICON_STATE; 24 import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; 25 import static com.android.launcher3.model.LoaderTask.SMARTSPACE_ON_HOME_SCREEN; 26 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 27 import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; 28 import static com.android.launcher3.util.SettingsCache.PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI; 29 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.IntentFilter; 34 import android.content.SharedPreferences; 35 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 36 import android.content.pm.LauncherApps; 37 import android.content.pm.LauncherApps.ArchiveCompatibilityParams; 38 import android.os.UserHandle; 39 import android.util.Log; 40 41 import androidx.annotation.Nullable; 42 import androidx.core.os.BuildCompat; 43 44 import com.android.launcher3.graphics.IconShape; 45 import com.android.launcher3.icons.IconCache; 46 import com.android.launcher3.icons.IconProvider; 47 import com.android.launcher3.icons.LauncherIconProvider; 48 import com.android.launcher3.icons.LauncherIcons; 49 import com.android.launcher3.model.ModelLauncherCallbacks; 50 import com.android.launcher3.notification.NotificationListener; 51 import com.android.launcher3.pm.InstallSessionHelper; 52 import com.android.launcher3.pm.InstallSessionTracker; 53 import com.android.launcher3.pm.UserCache; 54 import com.android.launcher3.util.LockedUserState; 55 import com.android.launcher3.util.MainThreadInitializedObject; 56 import com.android.launcher3.util.PackageManagerHelper; 57 import com.android.launcher3.util.Preconditions; 58 import com.android.launcher3.util.RunnableList; 59 import com.android.launcher3.util.SafeCloseable; 60 import com.android.launcher3.util.SettingsCache; 61 import com.android.launcher3.util.SimpleBroadcastReceiver; 62 import com.android.launcher3.util.Themes; 63 import com.android.launcher3.util.TraceHelper; 64 import com.android.launcher3.widget.custom.CustomWidgetManager; 65 66 public class LauncherAppState implements SafeCloseable { 67 68 public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; 69 70 // We do not need any synchronization for this variable as its only written on UI thread. 71 public static final MainThreadInitializedObject<LauncherAppState> INSTANCE = 72 new MainThreadInitializedObject<>(LauncherAppState::new); 73 74 private final Context mContext; 75 private final LauncherModel mModel; 76 private final LauncherIconProvider mIconProvider; 77 private final IconCache mIconCache; 78 private final InvariantDeviceProfile mInvariantDeviceProfile; 79 private boolean mIsSafeModeEnabled; 80 81 private final RunnableList mOnTerminateCallback = new RunnableList(); 82 getInstance(Context context)83 public static LauncherAppState getInstance(Context context) { 84 return INSTANCE.get(context); 85 } 86 getContext()87 public Context getContext() { 88 return mContext; 89 } 90 91 @SuppressWarnings("NewApi") LauncherAppState(Context context)92 public LauncherAppState(Context context) { 93 this(context, LauncherFiles.APP_ICONS_DB); 94 Log.v(Launcher.TAG, "LauncherAppState initiated"); 95 Preconditions.assertUIThread(); 96 97 mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", 98 () -> context.getPackageManager().isSafeMode()); 99 mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> { 100 if (modelPropertiesChanged) { 101 refreshAndReloadLauncher(); 102 } 103 }); 104 105 ModelLauncherCallbacks callbacks = mModel.newModelCallbacks(); 106 LauncherApps launcherApps = mContext.getSystemService(LauncherApps.class); 107 launcherApps.registerCallback(callbacks); 108 mOnTerminateCallback.add(() -> 109 mContext.getSystemService(LauncherApps.class).unregisterCallback(callbacks)); 110 111 if (BuildCompat.isAtLeastV() && Flags.enableSupportForArchiving()) { 112 ArchiveCompatibilityParams params = new ArchiveCompatibilityParams(); 113 params.setEnableUnarchivalConfirmation(false); 114 launcherApps.setArchiveCompatibility(params); 115 } 116 117 SimpleBroadcastReceiver modelChangeReceiver = 118 new SimpleBroadcastReceiver(mModel::onBroadcastIntent); 119 modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED, 120 ACTION_DEVICE_POLICY_RESOURCE_UPDATED); 121 if (BuildConfig.IS_STUDIO_BUILD) { 122 mContext.registerReceiver(modelChangeReceiver, new IntentFilter(ACTION_FORCE_ROLOAD), 123 RECEIVER_EXPORTED); 124 } 125 mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver)); 126 127 SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext) 128 .addUserEventListener(mModel::onUserEvent); 129 mOnTerminateCallback.add(userChangeListener::close); 130 131 if (enableSmartspaceRemovalToggle()) { 132 OnSharedPreferenceChangeListener firstPagePinnedItemListener = 133 new OnSharedPreferenceChangeListener() { 134 @Override 135 public void onSharedPreferenceChanged( 136 SharedPreferences sharedPreferences, String key) { 137 if (SMARTSPACE_ON_HOME_SCREEN.equals(key)) { 138 mModel.forceReload(); 139 } 140 } 141 }; 142 LauncherPrefs.getPrefs(mContext).registerOnSharedPreferenceChangeListener( 143 firstPagePinnedItemListener); 144 mOnTerminateCallback.add(() -> LauncherPrefs.getPrefs(mContext) 145 .unregisterOnSharedPreferenceChangeListener(firstPagePinnedItemListener)); 146 } 147 148 LockedUserState.get(context).runOnUserUnlocked(() -> { 149 CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext); 150 cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); 151 mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null)); 152 153 IconObserver observer = new IconObserver(); 154 SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener( 155 observer, MODEL_EXECUTOR.getHandler()); 156 mOnTerminateCallback.add(iconChangeTracker::close); 157 MODEL_EXECUTOR.execute(observer::verifyIconChanged); 158 LauncherPrefs.get(context).addListener(observer, THEMED_ICONS); 159 mOnTerminateCallback.add( 160 () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS)); 161 162 InstallSessionTracker installSessionTracker = 163 InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel); 164 mOnTerminateCallback.add(installSessionTracker::unregister); 165 }); 166 167 // Register an observer to rebind the notification listener when dots are re-enabled. 168 SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext); 169 SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged; 170 settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister); 171 onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI)); 172 mOnTerminateCallback.add(() -> 173 settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationLister)); 174 // Register an observer to notify Launcher about Private Space settings toggle. 175 registerPrivateSpaceHideWhenLockListener(settingsCache); 176 } 177 LauncherAppState(Context context, @Nullable String iconCacheFileName)178 public LauncherAppState(Context context, @Nullable String iconCacheFileName) { 179 mContext = context; 180 181 mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); 182 mIconProvider = new LauncherIconProvider(context); 183 mIconCache = new IconCache(mContext, mInvariantDeviceProfile, 184 iconCacheFileName, mIconProvider); 185 mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext), 186 PackageManagerHelper.INSTANCE.get(context), iconCacheFileName != null); 187 mOnTerminateCallback.add(mIconCache::close); 188 mOnTerminateCallback.add(mModel::destroy); 189 } 190 onNotificationSettingsChanged(boolean areNotificationDotsEnabled)191 private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { 192 if (areNotificationDotsEnabled) { 193 NotificationListener.requestRebind(new ComponentName( 194 mContext, NotificationListener.class)); 195 } 196 } 197 registerPrivateSpaceHideWhenLockListener(SettingsCache settingsCache)198 private void registerPrivateSpaceHideWhenLockListener(SettingsCache settingsCache) { 199 SettingsCache.OnChangeListener psHideWhenLockChangedListener = 200 this::onPrivateSpaceHideWhenLockChanged; 201 settingsCache.register(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, psHideWhenLockChangedListener); 202 mOnTerminateCallback.add(() -> settingsCache.unregister(PRIVATE_SPACE_HIDE_WHEN_LOCKED_URI, 203 psHideWhenLockChangedListener)); 204 } 205 onPrivateSpaceHideWhenLockChanged(boolean isPrivateSpaceHideOnLockEnabled)206 private void onPrivateSpaceHideWhenLockChanged(boolean isPrivateSpaceHideOnLockEnabled) { 207 mModel.forceReload(); 208 } 209 refreshAndReloadLauncher()210 private void refreshAndReloadLauncher() { 211 LauncherIcons.clearPool(mContext); 212 mIconCache.updateIconParams( 213 mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize); 214 mModel.forceReload(); 215 } 216 217 /** 218 * Call from Application.onTerminate(), which is not guaranteed to ever be called. 219 */ 220 @Override close()221 public void close() { 222 mOnTerminateCallback.executeAllAndDestroy(); 223 } 224 getIconProvider()225 public IconProvider getIconProvider() { 226 return mIconProvider; 227 } 228 getIconCache()229 public IconCache getIconCache() { 230 return mIconCache; 231 } 232 getModel()233 public LauncherModel getModel() { 234 return mModel; 235 } 236 getInvariantDeviceProfile()237 public InvariantDeviceProfile getInvariantDeviceProfile() { 238 return mInvariantDeviceProfile; 239 } 240 isSafeModeEnabled()241 public boolean isSafeModeEnabled() { 242 return mIsSafeModeEnabled; 243 } 244 245 /** 246 * Shorthand for {@link #getInvariantDeviceProfile()} 247 */ getIDP(Context context)248 public static InvariantDeviceProfile getIDP(Context context) { 249 return InvariantDeviceProfile.INSTANCE.get(context); 250 } 251 252 private class IconObserver 253 implements IconProvider.IconChangeListener, OnSharedPreferenceChangeListener { 254 255 @Override onAppIconChanged(String packageName, UserHandle user)256 public void onAppIconChanged(String packageName, UserHandle user) { 257 mModel.onAppIconChanged(packageName, user); 258 } 259 260 @Override onSystemIconStateChanged(String iconState)261 public void onSystemIconStateChanged(String iconState) { 262 IconShape.INSTANCE.get(mContext).pickBestShape(mContext); 263 refreshAndReloadLauncher(); 264 LauncherPrefs.get(mContext).put(ICON_STATE, iconState); 265 } 266 verifyIconChanged()267 void verifyIconChanged() { 268 String iconState = mIconProvider.getSystemIconState(); 269 if (!iconState.equals(LauncherPrefs.get(mContext).get(ICON_STATE))) { 270 onSystemIconStateChanged(iconState); 271 } 272 } 273 274 @Override onSharedPreferenceChanged(SharedPreferences prefs, String key)275 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 276 if (Themes.KEY_THEMED_ICONS.equals(key)) { 277 mIconProvider.setIconThemeSupported(Themes.isThemedIconEnabled(mContext)); 278 verifyIconChanged(); 279 } 280 } 281 } 282 } 283