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 com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_ICON_PARAMS; 20 import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; 21 import static com.android.launcher3.util.SecureSettingsObserver.newNotificationSettingsObserver; 22 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.LauncherApps; 27 import android.os.Handler; 28 import android.util.Log; 29 30 import androidx.annotation.Nullable; 31 32 import com.android.launcher3.config.FeatureFlags; 33 import com.android.launcher3.icons.IconCache; 34 import com.android.launcher3.icons.IconProvider; 35 import com.android.launcher3.icons.LauncherIcons; 36 import com.android.launcher3.model.PredictionModel; 37 import com.android.launcher3.notification.NotificationListener; 38 import com.android.launcher3.pm.InstallSessionHelper; 39 import com.android.launcher3.pm.InstallSessionTracker; 40 import com.android.launcher3.pm.UserCache; 41 import com.android.launcher3.util.MainThreadInitializedObject; 42 import com.android.launcher3.util.Preconditions; 43 import com.android.launcher3.util.SafeCloseable; 44 import com.android.launcher3.util.SecureSettingsObserver; 45 import com.android.launcher3.util.SimpleBroadcastReceiver; 46 import com.android.launcher3.widget.custom.CustomWidgetManager; 47 48 public class LauncherAppState { 49 50 public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher"; 51 52 // We do not need any synchronization for this variable as its only written on UI thread. 53 public static final MainThreadInitializedObject<LauncherAppState> INSTANCE = 54 new MainThreadInitializedObject<>(LauncherAppState::new); 55 56 private final Context mContext; 57 private final LauncherModel mModel; 58 private final IconCache mIconCache; 59 private final WidgetPreviewLoader mWidgetCache; 60 private final InvariantDeviceProfile mInvariantDeviceProfile; 61 private final PredictionModel mPredictionModel; 62 63 private SecureSettingsObserver mNotificationDotsObserver; 64 private InstallSessionTracker mInstallSessionTracker; 65 private SimpleBroadcastReceiver mModelChangeReceiver; 66 private SafeCloseable mCalendarChangeTracker; 67 private SafeCloseable mUserChangeListener; 68 getInstance(final Context context)69 public static LauncherAppState getInstance(final Context context) { 70 return INSTANCE.get(context); 71 } 72 getInstanceNoCreate()73 public static LauncherAppState getInstanceNoCreate() { 74 return INSTANCE.getNoCreate(); 75 } 76 getContext()77 public Context getContext() { 78 return mContext; 79 } 80 LauncherAppState(Context context)81 public LauncherAppState(Context context) { 82 this(context, LauncherFiles.APP_ICONS_DB); 83 84 mModelChangeReceiver = new SimpleBroadcastReceiver(mModel::onBroadcastIntent); 85 86 mContext.getSystemService(LauncherApps.class).registerCallback(mModel); 87 mModelChangeReceiver.register(mContext, Intent.ACTION_LOCALE_CHANGED, 88 Intent.ACTION_MANAGED_PROFILE_AVAILABLE, 89 Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE, 90 Intent.ACTION_MANAGED_PROFILE_UNLOCKED); 91 if (FeatureFlags.IS_STUDIO_BUILD) { 92 mModelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD); 93 } 94 95 mCalendarChangeTracker = IconProvider.registerIconChangeListener(mContext, 96 mModel::onAppIconChanged, MODEL_EXECUTOR.getHandler()); 97 98 // TODO: remove listener on terminate 99 FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload); 100 CustomWidgetManager.INSTANCE.get(mContext) 101 .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts); 102 103 mUserChangeListener = UserCache.INSTANCE.get(mContext) 104 .addUserChangeListener(mModel::forceReload); 105 106 mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged); 107 new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context)); 108 109 mInstallSessionTracker = InstallSessionHelper.INSTANCE.get(context) 110 .registerInstallTracker(mModel, MODEL_EXECUTOR); 111 112 if (!mContext.getResources().getBoolean(R.bool.notification_dots_enabled)) { 113 mNotificationDotsObserver = null; 114 } else { 115 // Register an observer to rebind the notification listener when dots are re-enabled. 116 mNotificationDotsObserver = 117 newNotificationSettingsObserver(mContext, this::onNotificationSettingsChanged); 118 mNotificationDotsObserver.register(); 119 mNotificationDotsObserver.dispatchOnChange(); 120 } 121 } 122 LauncherAppState(Context context, @Nullable String iconCacheFileName)123 public LauncherAppState(Context context, @Nullable String iconCacheFileName) { 124 Log.v(Launcher.TAG, "LauncherAppState initiated"); 125 Preconditions.assertUIThread(); 126 mContext = context; 127 128 mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context); 129 mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName); 130 mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache); 131 mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext)); 132 mPredictionModel = PredictionModel.newInstance(mContext); 133 } 134 onNotificationSettingsChanged(boolean areNotificationDotsEnabled)135 protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) { 136 if (areNotificationDotsEnabled) { 137 NotificationListener.requestRebind(new ComponentName( 138 mContext, NotificationListener.class)); 139 } 140 } 141 onIdpChanged(int changeFlags, InvariantDeviceProfile idp)142 private void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) { 143 if (changeFlags == 0) { 144 return; 145 } 146 147 if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) != 0) { 148 LauncherIcons.clearPool(); 149 mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize); 150 mWidgetCache.refresh(); 151 } 152 153 mModel.forceReload(); 154 } 155 156 /** 157 * Call from Application.onTerminate(), which is not guaranteed to ever be called. 158 */ onTerminate()159 public void onTerminate() { 160 if (mModelChangeReceiver != null) { 161 mContext.unregisterReceiver(mModelChangeReceiver); 162 } 163 mContext.getSystemService(LauncherApps.class).unregisterCallback(mModel); 164 if (mInstallSessionTracker != null) { 165 mInstallSessionTracker.unregister(); 166 } 167 if (mCalendarChangeTracker != null) { 168 mCalendarChangeTracker.close(); 169 } 170 if (mUserChangeListener != null) { 171 mUserChangeListener.close(); 172 } 173 CustomWidgetManager.INSTANCE.get(mContext).setWidgetRefreshCallback(null); 174 175 if (mNotificationDotsObserver != null) { 176 mNotificationDotsObserver.unregister(); 177 } 178 } 179 getIconCache()180 public IconCache getIconCache() { 181 return mIconCache; 182 } 183 getModel()184 public LauncherModel getModel() { 185 return mModel; 186 } 187 getPredictionModel()188 public PredictionModel getPredictionModel() { 189 return mPredictionModel; 190 } 191 getWidgetCache()192 public WidgetPreviewLoader getWidgetCache() { 193 return mWidgetCache; 194 } 195 getInvariantDeviceProfile()196 public InvariantDeviceProfile getInvariantDeviceProfile() { 197 return mInvariantDeviceProfile; 198 } 199 200 /** 201 * Shorthand for {@link #getInvariantDeviceProfile()} 202 */ getIDP(Context context)203 public static InvariantDeviceProfile getIDP(Context context) { 204 return InvariantDeviceProfile.INSTANCE.get(context); 205 } 206 } 207