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.SecureSettingsObserver.newNotificationSettingsObserver;
21 
22 import android.content.ComponentName;
23 import android.content.ContentProviderClient;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.os.Handler;
28 import android.util.Log;
29 
30 import com.android.launcher3.compat.LauncherAppsCompat;
31 import com.android.launcher3.compat.PackageInstallerCompat;
32 import com.android.launcher3.compat.UserManagerCompat;
33 import com.android.launcher3.config.FeatureFlags;
34 import com.android.launcher3.icons.IconCache;
35 import com.android.launcher3.icons.LauncherIcons;
36 import com.android.launcher3.notification.NotificationListener;
37 import com.android.launcher3.util.MainThreadInitializedObject;
38 import com.android.launcher3.util.Preconditions;
39 import com.android.launcher3.util.SecureSettingsObserver;
40 import com.android.launcher3.widget.custom.CustomWidgetManager;
41 
42 public class LauncherAppState {
43 
44     public static final String ACTION_FORCE_ROLOAD = "force-reload-launcher";
45 
46     // We do not need any synchronization for this variable as its only written on UI thread.
47     private static final MainThreadInitializedObject<LauncherAppState> INSTANCE =
48             new MainThreadInitializedObject<>(LauncherAppState::new);
49 
50     private final Context mContext;
51     private final LauncherModel mModel;
52     private final IconCache mIconCache;
53     private final WidgetPreviewLoader mWidgetCache;
54     private final InvariantDeviceProfile mInvariantDeviceProfile;
55     private final SecureSettingsObserver mNotificationDotsObserver;
56 
getInstance(final Context context)57     public static LauncherAppState getInstance(final Context context) {
58         return INSTANCE.get(context);
59     }
60 
getInstanceNoCreate()61     public static LauncherAppState getInstanceNoCreate() {
62         return INSTANCE.getNoCreate();
63     }
64 
getContext()65     public Context getContext() {
66         return mContext;
67     }
68 
LauncherAppState(Context context)69     private LauncherAppState(Context context) {
70         if (getLocalProvider(context) == null) {
71             throw new RuntimeException(
72                     "Initializing LauncherAppState in the absence of LauncherProvider");
73         }
74         Log.v(Launcher.TAG, "LauncherAppState initiated");
75         Preconditions.assertUIThread();
76         mContext = context;
77 
78         mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(mContext);
79         mIconCache = new IconCache(mContext, mInvariantDeviceProfile);
80         mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
81         mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
82 
83         LauncherAppsCompat.getInstance(mContext).addOnAppsChangedCallback(mModel);
84 
85         // Register intent receivers
86         IntentFilter filter = new IntentFilter();
87         filter.addAction(Intent.ACTION_LOCALE_CHANGED);
88         // For handling managed profiles
89         filter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
90         filter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
91         filter.addAction(Intent.ACTION_MANAGED_PROFILE_AVAILABLE);
92         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE);
93         filter.addAction(Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
94 
95         if (FeatureFlags.IS_DOGFOOD_BUILD) {
96             filter.addAction(ACTION_FORCE_ROLOAD);
97         }
98         FeatureFlags.APP_SEARCH_IMPROVEMENTS.addChangeListener(context, mModel::forceReload);
99 
100         mContext.registerReceiver(mModel, filter);
101         UserManagerCompat.getInstance(mContext).enableAndResetCache();
102         mInvariantDeviceProfile.addOnChangeListener(this::onIdpChanged);
103         new Handler().post( () -> mInvariantDeviceProfile.verifyConfigChangedInBackground(context));
104 
105         if (!mContext.getResources().getBoolean(R.bool.notification_dots_enabled)) {
106             mNotificationDotsObserver = null;
107         } else {
108             // Register an observer to rebind the notification listener when dots are re-enabled.
109             mNotificationDotsObserver =
110                     newNotificationSettingsObserver(mContext, this::onNotificationSettingsChanged);
111             mNotificationDotsObserver.register();
112             mNotificationDotsObserver.dispatchOnChange();
113         }
114     }
115 
onNotificationSettingsChanged(boolean areNotificationDotsEnabled)116     protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
117         if (areNotificationDotsEnabled) {
118             NotificationListener.requestRebind(new ComponentName(
119                     mContext, NotificationListener.class));
120         }
121     }
122 
onIdpChanged(int changeFlags, InvariantDeviceProfile idp)123     private void onIdpChanged(int changeFlags, InvariantDeviceProfile idp) {
124         if (changeFlags == 0) {
125             return;
126         }
127 
128         if ((changeFlags & CHANGE_FLAG_ICON_PARAMS) != 0) {
129             LauncherIcons.clearPool();
130             mIconCache.updateIconParams(idp.fillResIconDpi, idp.iconBitmapSize);
131             mWidgetCache.refresh();
132         }
133 
134         mModel.forceReload();
135     }
136 
137     /**
138      * Call from Application.onTerminate(), which is not guaranteed to ever be called.
139      */
onTerminate()140     public void onTerminate() {
141         mContext.unregisterReceiver(mModel);
142         final LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext);
143         launcherApps.removeOnAppsChangedCallback(mModel);
144         PackageInstallerCompat.getInstance(mContext).onStop();
145         if (mNotificationDotsObserver != null) {
146             mNotificationDotsObserver.unregister();
147         }
148     }
149 
setLauncher(Launcher launcher)150     LauncherModel setLauncher(Launcher launcher) {
151         getLocalProvider(mContext).setLauncherProviderChangeListener(launcher);
152         mModel.initialize(launcher);
153         CustomWidgetManager.INSTANCE.get(launcher)
154                 .setWidgetRefreshCallback(mModel::refreshAndBindWidgetsAndShortcuts);
155         return mModel;
156     }
157 
getIconCache()158     public IconCache getIconCache() {
159         return mIconCache;
160     }
161 
getModel()162     public LauncherModel getModel() {
163         return mModel;
164     }
165 
getWidgetCache()166     public WidgetPreviewLoader getWidgetCache() {
167         return mWidgetCache;
168     }
169 
getInvariantDeviceProfile()170     public InvariantDeviceProfile getInvariantDeviceProfile() {
171         return mInvariantDeviceProfile;
172     }
173 
174     /**
175      * Shorthand for {@link #getInvariantDeviceProfile()}
176      */
getIDP(Context context)177     public static InvariantDeviceProfile getIDP(Context context) {
178         return InvariantDeviceProfile.INSTANCE.get(context);
179     }
180 
getLocalProvider(Context context)181     private static LauncherProvider getLocalProvider(Context context) {
182         try (ContentProviderClient cl = context.getContentResolver()
183                 .acquireContentProviderClient(LauncherProvider.AUTHORITY)) {
184             return (LauncherProvider) cl.getLocalContentProvider();
185         }
186     }
187 }
188