• 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 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