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