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