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