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