1 /* 2 * Copyright (C) 2015 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.util; 18 19 import android.content.Context; 20 import android.content.SharedPreferences; 21 import android.content.pm.LauncherActivityInfo; 22 import android.os.Process; 23 import android.os.UserHandle; 24 import android.support.v4.os.BuildCompat; 25 26 import com.android.launcher3.AppInfo; 27 import com.android.launcher3.FolderInfo; 28 import com.android.launcher3.IconCache; 29 import com.android.launcher3.ItemInfo; 30 import com.android.launcher3.LauncherAppState; 31 import com.android.launcher3.LauncherFiles; 32 import com.android.launcher3.LauncherModel; 33 import com.android.launcher3.MainThreadExecutor; 34 import com.android.launcher3.R; 35 import com.android.launcher3.SessionCommitReceiver; 36 import com.android.launcher3.ShortcutInfo; 37 import com.android.launcher3.compat.UserManagerCompat; 38 import com.android.launcher3.shortcuts.ShortcutInfoCompat; 39 40 import java.util.ArrayList; 41 import java.util.HashSet; 42 import java.util.List; 43 44 /** 45 * Handles addition of app shortcuts for managed profiles. 46 * Methods of class should only be called on {@link LauncherModel#sWorkerThread}. 47 */ 48 public class ManagedProfileHeuristic { 49 50 /** 51 * Maintain a set of packages installed per user. 52 */ 53 private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_"; 54 55 private static final String USER_FOLDER_ID_PREFIX = "user_folder_"; 56 57 /** 58 * Duration (in milliseconds) for which app shortcuts will be added to work folder. 59 */ 60 private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000; 61 get(Context context, UserHandle user)62 public static ManagedProfileHeuristic get(Context context, UserHandle user) { 63 if (!Process.myUserHandle().equals(user)) { 64 return new ManagedProfileHeuristic(context, user); 65 } 66 return null; 67 } 68 69 private final Context mContext; 70 private final LauncherModel mModel; 71 private final UserHandle mUser; 72 private final IconCache mIconCache; 73 private final boolean mAddIconsToHomescreen; 74 ManagedProfileHeuristic(Context context, UserHandle user)75 private ManagedProfileHeuristic(Context context, UserHandle user) { 76 mContext = context; 77 mUser = user; 78 mModel = LauncherAppState.getInstance(context).getModel(); 79 mIconCache = LauncherAppState.getInstance(context).getIconCache(); 80 mAddIconsToHomescreen = 81 !BuildCompat.isAtLeastO() || SessionCommitReceiver.isEnabled(context); 82 } 83 processPackageRemoved(String[] packages)84 public void processPackageRemoved(String[] packages) { 85 Preconditions.assertWorkerThread(); 86 ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler(); 87 for (String pkg : packages) { 88 handler.onPackageRemoved(pkg, mUser); 89 } 90 } 91 processPackageAdd(String[] packages)92 public void processPackageAdd(String[] packages) { 93 Preconditions.assertWorkerThread(); 94 ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler(); 95 for (String pkg : packages) { 96 handler.onPackageAdded(pkg, mUser); 97 } 98 } 99 processUserApps(List<LauncherActivityInfo> apps)100 public void processUserApps(List<LauncherActivityInfo> apps) { 101 Preconditions.assertWorkerThread(); 102 new ManagedProfilePackageHandler().processUserApps(apps, mUser); 103 } 104 105 private class ManagedProfilePackageHandler extends CachedPackageTracker { 106 ManagedProfilePackageHandler()107 private ManagedProfilePackageHandler() { 108 super(mContext, LauncherFiles.MANAGED_USER_PREFERENCES_KEY); 109 } 110 onLauncherAppsAdded( List<LauncherActivityInstallInfo> apps, UserHandle user, boolean userAppsExisted)111 protected void onLauncherAppsAdded( 112 List<LauncherActivityInstallInfo> apps, UserHandle user, boolean userAppsExisted) { 113 ArrayList<ShortcutInfo> workFolderApps = new ArrayList<>(); 114 ArrayList<ShortcutInfo> homescreenApps = new ArrayList<>(); 115 116 int count = apps.size(); 117 long folderCreationTime = 118 mUserManager.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION; 119 120 boolean quietModeEnabled = UserManagerCompat.getInstance(mContext) 121 .isQuietModeEnabled(user); 122 for (int i = 0; i < count; i++) { 123 LauncherActivityInstallInfo info = apps.get(i); 124 AppInfo appInfo = new AppInfo(info.info, user, quietModeEnabled); 125 mIconCache.getTitleAndIcon(appInfo, info.info, false /* useLowResIcon */); 126 ShortcutInfo si = appInfo.makeShortcut(); 127 ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si); 128 } 129 130 finalizeWorkFolder(user, workFolderApps, homescreenApps); 131 132 // Do not add shortcuts on the homescreen for the first time. This prevents the launcher 133 // getting filled with the managed user apps, when it start with a fresh DB (or after 134 // a very long time). 135 if (userAppsExisted && !homescreenApps.isEmpty() && mAddIconsToHomescreen) { 136 mModel.addAndBindAddedWorkspaceItems(new ArrayList<ItemInfo>(homescreenApps)); 137 } 138 } 139 140 @Override onLauncherPackageRemoved(String packageName, UserHandle user)141 protected void onLauncherPackageRemoved(String packageName, UserHandle user) { 142 } 143 144 /** 145 * Adds and binds shortcuts marked to be added to the work folder. 146 */ finalizeWorkFolder( UserHandle user, final ArrayList<ShortcutInfo> workFolderApps, ArrayList<ShortcutInfo> homescreenApps)147 private void finalizeWorkFolder( 148 UserHandle user, final ArrayList<ShortcutInfo> workFolderApps, 149 ArrayList<ShortcutInfo> homescreenApps) { 150 if (workFolderApps.isEmpty()) { 151 return; 152 } 153 // Try to get a work folder. 154 String folderIdKey = USER_FOLDER_ID_PREFIX + mUserManager.getSerialNumberForUser(user); 155 if (!mAddIconsToHomescreen) { 156 if (!mPrefs.contains(folderIdKey)) { 157 // Just mark the folder id preference to avoid new folder creation later. 158 mPrefs.edit().putLong(folderIdKey, -1).apply(); 159 } 160 return; 161 } 162 if (mPrefs.contains(folderIdKey)) { 163 long folderId = mPrefs.getLong(folderIdKey, 0); 164 final FolderInfo workFolder = mModel.findFolderById(folderId); 165 166 if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) { 167 // Could not get a work folder. Add all the icons to homescreen. 168 homescreenApps.addAll(0, workFolderApps); 169 return; 170 } 171 saveWorkFolderShortcuts(folderId, workFolder.contents.size(), workFolderApps); 172 173 // FolderInfo could already be bound. We need to add shortcuts on the UI thread. 174 new MainThreadExecutor().execute(new Runnable() { 175 176 @Override 177 public void run() { 178 workFolder.prepareAutoUpdate(); 179 for (ShortcutInfo info : workFolderApps) { 180 workFolder.add(info, false); 181 } 182 } 183 }); 184 } else { 185 // Create a new folder. 186 final FolderInfo workFolder = new FolderInfo(); 187 workFolder.title = mContext.getText(R.string.work_folder_name); 188 workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null); 189 190 // Add all shortcuts before adding it to the UI, as an empty folder might get deleted. 191 for (ShortcutInfo info : workFolderApps) { 192 workFolder.add(info, false); 193 } 194 195 // Add the item to home screen and DB. This also generates an item id synchronously. 196 ArrayList<ItemInfo> itemList = new ArrayList<>(1); 197 itemList.add(workFolder); 198 mModel.addAndBindAddedWorkspaceItems(itemList); 199 mPrefs.edit().putLong(folderIdKey, workFolder.id).apply(); 200 201 saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps); 202 } 203 } 204 205 @Override onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts, UserHandle user)206 public void onShortcutsChanged(String packageName, List<ShortcutInfoCompat> shortcuts, 207 UserHandle user) { 208 // Do nothing 209 } 210 } 211 212 /** 213 * Add work folder shortcuts to the DB. 214 */ saveWorkFolderShortcuts( long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps)215 private void saveWorkFolderShortcuts( 216 long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) { 217 for (ItemInfo info : workFolderApps) { 218 info.rank = startingRank++; 219 mModel.getWriter(false).addItemToDatabase(info, workFolderId, 0, 0, 0); 220 } 221 } 222 223 /** 224 * Verifies that entries corresponding to {@param users} exist and removes all invalid entries. 225 */ processAllUsers(List<UserHandle> users, Context context)226 public static void processAllUsers(List<UserHandle> users, Context context) { 227 UserManagerCompat userManager = UserManagerCompat.getInstance(context); 228 HashSet<String> validKeys = new HashSet<String>(); 229 for (UserHandle user : users) { 230 addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys); 231 } 232 233 SharedPreferences prefs = context.getSharedPreferences( 234 LauncherFiles.MANAGED_USER_PREFERENCES_KEY, 235 Context.MODE_PRIVATE); 236 SharedPreferences.Editor editor = prefs.edit(); 237 for (String key : prefs.getAll().keySet()) { 238 if (!validKeys.contains(key)) { 239 editor.remove(key); 240 } 241 } 242 editor.apply(); 243 } 244 addAllUserKeys(long userSerial, HashSet<String> keysOut)245 private static void addAllUserKeys(long userSerial, HashSet<String> keysOut) { 246 keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial); 247 keysOut.add(USER_FOLDER_ID_PREFIX + userSerial); 248 } 249 250 /** 251 * For each user, if a work folder has not been created, mark it such that the folder will 252 * never get created. 253 */ markExistingUsersForNoFolderCreation(Context context)254 public static void markExistingUsersForNoFolderCreation(Context context) { 255 UserManagerCompat userManager = UserManagerCompat.getInstance(context); 256 UserHandle myUser = Process.myUserHandle(); 257 258 SharedPreferences prefs = null; 259 for (UserHandle user : userManager.getUserProfiles()) { 260 if (myUser.equals(user)) { 261 continue; 262 } 263 264 if (prefs == null) { 265 prefs = context.getSharedPreferences( 266 LauncherFiles.MANAGED_USER_PREFERENCES_KEY, 267 Context.MODE_PRIVATE); 268 } 269 String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user); 270 if (!prefs.contains(folderIdKey)) { 271 prefs.edit().putLong(folderIdKey, ItemInfo.NO_ID).apply(); 272 } 273 } 274 } 275 } 276