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