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.LauncherPrefs.ICON_STATE; 23 import static com.android.launcher3.LauncherPrefs.THEMED_ICONS; 24 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 25 import static com.android.launcher3.util.SettingsCache.NOTIFICATION_BADGING_URI; 26 27 import android.content.ComponentName; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.SharedPreferences; 32 import android.content.SharedPreferences.OnSharedPreferenceChangeListener; 33 import android.content.pm.LauncherApps; 34 import android.os.UserHandle; 35 import android.util.Log; 36 import android.util.SparseArray; 37 import android.widget.RemoteViews; 38 39 import androidx.annotation.GuardedBy; 40 import androidx.annotation.NonNull; 41 import androidx.annotation.Nullable; 42 43 import com.android.launcher3.graphics.IconShape; 44 import com.android.launcher3.icons.IconCache; 45 import com.android.launcher3.icons.IconProvider; 46 import com.android.launcher3.icons.LauncherIconProvider; 47 import com.android.launcher3.icons.LauncherIcons; 48 import com.android.launcher3.notification.NotificationListener; 49 import com.android.launcher3.pm.InstallSessionHelper; 50 import com.android.launcher3.pm.InstallSessionTracker; 51 import com.android.launcher3.pm.UserCache; 52 import com.android.launcher3.util.LockedUserState; 53 import com.android.launcher3.util.MainThreadInitializedObject; 54 import com.android.launcher3.util.Preconditions; 55 import com.android.launcher3.util.RunnableList; 56 import com.android.launcher3.util.SafeCloseable; 57 import com.android.launcher3.util.SettingsCache; 58 import com.android.launcher3.util.SimpleBroadcastReceiver; 59 import com.android.launcher3.util.Themes; 60 import com.android.launcher3.widget.custom.CustomWidgetManager; 61 62 public class LauncherAppState implements SafeCloseable { 63 64 public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; 65 public static final String KEY_ICON_STATE = "pref_icon_shape_path"; 66 public static final String KEY_ALL_APPS_OVERVIEW_THRESHOLD = "pref_all_apps_overview_threshold"; 67 68 // We do not need any synchronization for this variable as its only written on UI thread. 69 public static final MainThreadInitializedObject<LauncherAppState> INSTANCE = 70 new MainThreadInitializedObject<>(LauncherAppState::new); 71 72 private final Context mContext; 73 private final LauncherModel mModel; 74 private final LauncherIconProvider mIconProvider; 75 private final IconCache mIconCache; 76 private final InvariantDeviceProfile mInvariantDeviceProfile; 77 private final RunnableList mOnTerminateCallback = new RunnableList(); 78 79 // WORKAROUND: b/269335387 remove this after widget background listener is enabled 80 /* Array of RemoteViews cached by Launcher process */ 81 @GuardedBy("itself") 82 @NonNull 83 public final SparseArray<RemoteViews> mCachedRemoteViews = new SparseArray<>(); 84 getInstance(final Context context)85 public static LauncherAppState getInstance(final Context context) { 86 return INSTANCE.get(context); 87 } 88 getInstanceNoCreate()89 public static LauncherAppState getInstanceNoCreate() { 90 return INSTANCE.getNoCreate(); 91 } 92 getContext()93 public Context getContext() { 94 return mContext; 95 } 96 LauncherAppState(Context context)97 public LauncherAppState(Context context) { 98 this(context, LauncherFiles.APP_ICONS_DB); 99 Log.v(Launcher.TAG, "LauncherAppState initiated"); 100 Preconditions.assertUIThread(); 101 102 mInvariantDeviceProfile.addOnChangeListener(modelPropertiesChanged -> { 103 if (modelPropertiesChanged) { 104 refreshAndReloadLauncher(); 105 } 106 }); 107 108 mContext.getSystemService(LauncherApps.class).registerCallback(mModel); 109 mOnTerminateCallback.add(() -> 110 mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel)); 111 112 SimpleBroadcastReceiver modelChangeReceiver = 113 new SimpleBroadcastReceiver(mModel::onBroadcastIntent); 114 modelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED, 115 ACTION_DEVICE_POLICY_RESOURCE_UPDATED); 116 if (BuildConfig.IS_STUDIO_BUILD) { 117 mContext.registerReceiver(modelChangeReceiver, new IntentFilter(ACTION_FORCE_ROLOAD), 118 RECEIVER_EXPORTED); 119 } 120 mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver)); 121 122 SafeCloseable userChangeListener = UserCache.INSTANCE.get(mContext) 123 .addUserEventListener(mModel::onUserEvent); 124 mOnTerminateCallback.add(userChangeListener::close); 125 126 LockedUserState.get(context).runOnUserUnlocked(() -> { 127 CustomWidgetManager cwm = CustomWidgetManager.INSTANCE.get(mContext); 128 cwm.setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); 129 mOnTerminateCallback.add(() -> cwm.setWidgetRefreshCallback(null)); 130 131 IconObserver observer = new IconObserver(); 132 SafeCloseable iconChangeTracker = mIconProvider.registerIconChangeListener( 133 observer, MODEL_EXECUTOR.getHandler()); 134 mOnTerminateCallback.add(iconChangeTracker::close); 135 MODEL_EXECUTOR.execute(observer::verifyIconChanged); 136 LauncherPrefs.get(context).addListener(observer, THEMED_ICONS); 137 mOnTerminateCallback.add( 138 () -> LauncherPrefs.get(mContext).removeListener(observer, THEMED_ICONS)); 139 140 InstallSessionTracker installSessionTracker = 141 InstallSessionHelper.INSTANCE.get(context).registerInstallTracker(mModel); 142 mOnTerminateCallback.add(installSessionTracker::unregister); 143 }); 144 145 // Register an observer to rebind the notification listener when dots are re-enabled. 146 SettingsCache settingsCache = SettingsCache.INSTANCE.get(mContext); 147 SettingsCache.OnChangeListener notificationLister = this::onNotificationSettingsChanged; 148 settingsCache.register(NOTIFICATION_BADGING_URI, notificationLister); 149 onNotificationSettingsChanged(settingsCache.getValue(NOTIFICATION_BADGING_URI)); 150 mOnTerminateCallback.add(() -> 151 settingsCache.unregister(NOTIFICATION_BADGING_URI, notificationLister)); 152 } 153 LauncherAppState(Context context, @Nullable String iconCacheFileName)154 public LauncherAppState(Context context, @Nullable String iconCacheFileName) { 155 mContext = context; 156 157 mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); 158 mIconProvider = new LauncherIconProvider(context); 159 mIconCache = new IconCache(mContext, mInvariantDeviceProfile, 160 iconCacheFileName, mIconProvider); 161 mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext), 162 iconCacheFileName != null); 163 mOnTerminateCallback.add(mIconCache::close); 164 mOnTerminateCallback.add(mModel::destroy); 165 } 166 onNotificationSettingsChanged(boolean areNotificationDotsEnabled)167 private void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { 168 if (areNotificationDotsEnabled) { 169 NotificationListener.requestRebind(new ComponentName( 170 mContext, NotificationListener.class)); 171 } 172 } 173 refreshAndReloadLauncher()174 private void refreshAndReloadLauncher() { 175 LauncherIcons.clearPool(); 176 mIconCache.updateIconParams( 177 mInvariantDeviceProfile.fillResIconDpi, mInvariantDeviceProfile.iconBitmapSize); 178 mModel.forceReload(); 179 } 180 181 /** 182 * Call from Application.onTerminate(), which is not guaranteed to ever be called. 183 */ 184 @Override close()185 public void close() { 186 mOnTerminateCallback.executeAllAndDestroy(); 187 } 188 getIconProvider()189 public IconProvider getIconProvider() { 190 return mIconProvider; 191 } 192 getIconCache()193 public IconCache getIconCache() { 194 return mIconCache; 195 } 196 getModel()197 public LauncherModel getModel() { 198 return mModel; 199 } 200 getInvariantDeviceProfile()201 public InvariantDeviceProfile getInvariantDeviceProfile() { 202 return mInvariantDeviceProfile; 203 } 204 205 /** 206 * Shorthand for {@link #getInvariantDeviceProfile()} 207 */ getIDP(Context context)208 public static InvariantDeviceProfile getIDP(Context context) { 209 return InvariantDeviceProfile.INSTANCE.get(context); 210 } 211 212 private class IconObserver 213 implements IconProvider.IconChangeListener, OnSharedPreferenceChangeListener { 214 215 @Override onAppIconChanged(String packageName, UserHandle user)216 public void onAppIconChanged(String packageName, UserHandle user) { 217 mModel.onAppIconChanged(packageName, user); 218 } 219 220 @Override onSystemIconStateChanged(String iconState)221 public void onSystemIconStateChanged(String iconState) { 222 IconShape.init(mContext); 223 refreshAndReloadLauncher(); 224 LauncherPrefs.get(mContext).put(ICON_STATE, iconState); 225 } 226 verifyIconChanged()227 void verifyIconChanged() { 228 String iconState = mIconProvider.getSystemIconState(); 229 if (!iconState.equals(LauncherPrefs.get(mContext).get(ICON_STATE))) { 230 onSystemIconStateChanged(iconState); 231 } 232 } 233 234 @Override onSharedPreferenceChanged(SharedPreferences prefs, String key)235 public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { 236 if (Themes.KEY_THEMED_ICONS.equals(key)) { 237 mIconProvider.setIconThemeSupported(Themes.isThemedIconEnabled(mContext)); 238 verifyIconChanged(); 239 } 240 } 241 } 242 } 243