ManagedProfileHeuristic.java revision a214a63bfe365a0e81bc3082d56896c6df24d0b4
1package com.android.launcher3.util; 2 3import android.content.Context; 4import android.content.SharedPreferences; 5import android.content.pm.PackageInfo; 6import android.content.pm.PackageManager; 7import android.content.pm.PackageManager.NameNotFoundException; 8import android.util.Log; 9 10import com.android.launcher3.FolderInfo; 11import com.android.launcher3.ItemInfo; 12import com.android.launcher3.LauncherAppState; 13import com.android.launcher3.LauncherFiles; 14import com.android.launcher3.LauncherModel; 15import com.android.launcher3.MainThreadExecutor; 16import com.android.launcher3.R; 17import com.android.launcher3.ShortcutInfo; 18import com.android.launcher3.Utilities; 19import com.android.launcher3.compat.LauncherActivityInfoCompat; 20import com.android.launcher3.compat.LauncherAppsCompat; 21import com.android.launcher3.compat.UserHandleCompat; 22import com.android.launcher3.compat.UserManagerCompat; 23 24import java.util.ArrayList; 25import java.util.Collections; 26import java.util.Comparator; 27import java.util.HashSet; 28import java.util.List; 29import java.util.Set; 30 31/** 32 * Handles addition of app shortcuts for managed profiles. 33 * Methods of class should only be called on {@link LauncherModel#sWorkerThread}. 34 */ 35public class ManagedProfileHeuristic { 36 37 private static final String TAG = "ManagedProfileHeuristic"; 38 39 /** 40 * Maintain a set of packages installed per user. 41 */ 42 private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_"; 43 44 private static final String USER_FOLDER_ID_PREFIX = "user_folder_"; 45 46 /** 47 * Duration (in milliseconds) for which app shortcuts will be added to work folder. 48 */ 49 private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000; 50 51 public static ManagedProfileHeuristic get(Context context, UserHandleCompat user) { 52 if (Utilities.isLmpOrAbove() && !UserHandleCompat.myUserHandle().equals(user)) { 53 return new ManagedProfileHeuristic(context, user); 54 } 55 return null; 56 } 57 58 private final Context mContext; 59 private final UserHandleCompat mUser; 60 private final LauncherModel mModel; 61 62 private final SharedPreferences mPrefs; 63 private final long mUserSerial; 64 private final long mUserCreationTime; 65 private final String mPackageSetKey; 66 67 private ArrayList<ShortcutInfo> mHomescreenApps; 68 private ArrayList<ShortcutInfo> mWorkFolderApps; 69 70 private ManagedProfileHeuristic(Context context, UserHandleCompat user) { 71 mContext = context; 72 mUser = user; 73 mModel = LauncherAppState.getInstance().getModel(); 74 75 UserManagerCompat userManager = UserManagerCompat.getInstance(context); 76 mUserSerial = userManager.getSerialNumberForUser(user); 77 mUserCreationTime = userManager.getUserCreationTime(user); 78 mPackageSetKey = INSTALLED_PACKAGES_PREFIX + mUserSerial; 79 80 mPrefs = mContext.getSharedPreferences(LauncherFiles.MANAGED_USER_PREFERENCES_KEY, 81 Context.MODE_PRIVATE); 82 } 83 84 /** 85 * Checks the list of user apps and adds icons for newly installed apps on the homescreen or 86 * workfolder. 87 */ 88 public void processUserApps(List<LauncherActivityInfoCompat> apps) { 89 mHomescreenApps = new ArrayList<>(); 90 mWorkFolderApps = new ArrayList<>(); 91 92 HashSet<String> packageSet = new HashSet<>(); 93 final boolean userAppsExisted = getUserApps(packageSet); 94 95 boolean newPackageAdded = false; 96 97 for (LauncherActivityInfoCompat info : apps) { 98 String packageName = info.getComponentName().getPackageName(); 99 if (!packageSet.contains(packageName)) { 100 packageSet.add(packageName); 101 newPackageAdded = true; 102 103 try { 104 PackageInfo pkgInfo = mContext.getPackageManager() 105 .getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); 106 markForAddition(info, pkgInfo.firstInstallTime); 107 } catch (NameNotFoundException e) { 108 Log.e(TAG, "Unknown package " + packageName, e); 109 } 110 } 111 } 112 113 if (newPackageAdded) { 114 mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); 115 // Do not add shortcuts on the homescreen for the first time. This prevents the launcher 116 // getting filled with the managed user apps, when it start with a fresh DB (or after 117 // a very long time). 118 finalizeAdditions(userAppsExisted); 119 } 120 } 121 122 private void markForAddition(LauncherActivityInfoCompat info, long installTime) { 123 ArrayList<ShortcutInfo> targetList = 124 (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ? 125 mWorkFolderApps : mHomescreenApps; 126 targetList.add(ShortcutInfo.fromActivityInfo(info, mContext)); 127 } 128 129 /** 130 * Adds and binds shortcuts marked to be added to the work folder. 131 */ 132 private void finalizeWorkFolder() { 133 if (mWorkFolderApps.isEmpty()) { 134 return; 135 } 136 Collections.sort(mWorkFolderApps, new Comparator<ShortcutInfo>() { 137 138 @Override 139 public int compare(ShortcutInfo lhs, ShortcutInfo rhs) { 140 return Long.compare(lhs.firstInstallTime, rhs.firstInstallTime); 141 } 142 }); 143 144 // Try to get a work folder. 145 String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial; 146 if (mPrefs.contains(folderIdKey)) { 147 long folderId = mPrefs.getLong(folderIdKey, 0); 148 final FolderInfo workFolder = mModel.findFolderById(folderId); 149 150 if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) { 151 // Could not get a work folder. Add all the icons to homescreen. 152 mHomescreenApps.addAll(mWorkFolderApps); 153 return; 154 } 155 saveWorkFolderShortcuts(folderId, workFolder.contents.size()); 156 157 final ArrayList<ShortcutInfo> shortcuts = mWorkFolderApps; 158 // FolderInfo could already be bound. We need to add shortcuts on the UI thread. 159 new MainThreadExecutor().execute(new Runnable() { 160 161 @Override 162 public void run() { 163 for (ShortcutInfo info : shortcuts) { 164 workFolder.add(info); 165 } 166 } 167 }); 168 } else { 169 // Create a new folder. 170 final FolderInfo workFolder = new FolderInfo(); 171 workFolder.title = mContext.getText(R.string.work_folder_name); 172 workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null); 173 174 // Add all shortcuts before adding it to the UI, as an empty folder might get deleted. 175 for (ShortcutInfo info : mWorkFolderApps) { 176 workFolder.add(info); 177 } 178 179 // Add the item to home screen and DB. This also generates an item id synchronously. 180 ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1); 181 itemList.add(workFolder); 182 mModel.addAndBindAddedWorkspaceItems(mContext, itemList); 183 mPrefs.edit().putLong(USER_FOLDER_ID_PREFIX + mUserSerial, workFolder.id).apply(); 184 185 saveWorkFolderShortcuts(workFolder.id, 0); 186 } 187 } 188 189 /** 190 * Add work folder shortcuts to the DB. 191 */ 192 private void saveWorkFolderShortcuts(long workFolderId, int startingRank) { 193 for (ItemInfo info : mWorkFolderApps) { 194 info.rank = startingRank++; 195 LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0); 196 } 197 } 198 199 /** 200 * Adds and binds all shortcuts marked for addition. 201 */ 202 private void finalizeAdditions(boolean addHomeScreenShortcuts) { 203 finalizeWorkFolder(); 204 205 if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) { 206 mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps); 207 } 208 } 209 210 /** 211 * Updates the list of installed apps and adds any new icons on homescreen or work folder. 212 */ 213 public void processPackageAdd(String[] packages) { 214 mHomescreenApps = new ArrayList<>(); 215 mWorkFolderApps = new ArrayList<>(); 216 217 HashSet<String> packageSet = new HashSet<>(); 218 final boolean userAppsExisted = getUserApps(packageSet); 219 220 boolean newPackageAdded = false; 221 long installTime = System.currentTimeMillis(); 222 LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext); 223 224 for (String packageName : packages) { 225 if (!packageSet.contains(packageName)) { 226 packageSet.add(packageName); 227 newPackageAdded = true; 228 229 List<LauncherActivityInfoCompat> activities = 230 launcherApps.getActivityList(packageName, mUser); 231 if (!activities.isEmpty()) { 232 markForAddition(activities.get(0), installTime); 233 } 234 } 235 } 236 237 if (newPackageAdded) { 238 mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); 239 finalizeAdditions(userAppsExisted); 240 } 241 } 242 243 /** 244 * Updates the list of installed packages for the user. 245 */ 246 public void processPackageRemoved(String[] packages) { 247 HashSet<String> packageSet = new HashSet<String>(); 248 getUserApps(packageSet); 249 boolean packageRemoved = false; 250 251 for (String packageName : packages) { 252 if (packageSet.remove(packageName)) { 253 packageRemoved = true; 254 } 255 } 256 257 if (packageRemoved) { 258 mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); 259 } 260 } 261 262 /** 263 * Reads the list of user apps which have already been processed. 264 * @return false if the list didn't exist, true otherwise 265 */ 266 private boolean getUserApps(HashSet<String> outExistingApps) { 267 Set<String> userApps = mPrefs.getStringSet(mPackageSetKey, null); 268 if (userApps == null) { 269 return false; 270 } else { 271 outExistingApps.addAll(userApps); 272 return true; 273 } 274 } 275 276 /** 277 * Verifies that entries corresponding to {@param users} exist and removes all invalid entries. 278 */ 279 public static void processAllUsers(List<UserHandleCompat> users, Context context) { 280 if (!Utilities.isLmpOrAbove()) { 281 return; 282 } 283 UserManagerCompat userManager = UserManagerCompat.getInstance(context); 284 HashSet<String> validKeys = new HashSet<String>(); 285 for (UserHandleCompat user : users) { 286 addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys); 287 } 288 289 SharedPreferences prefs = context.getSharedPreferences( 290 LauncherFiles.MANAGED_USER_PREFERENCES_KEY, 291 Context.MODE_PRIVATE); 292 SharedPreferences.Editor editor = prefs.edit(); 293 for (String key : prefs.getAll().keySet()) { 294 if (!validKeys.contains(key)) { 295 editor.remove(key); 296 } 297 } 298 editor.apply(); 299 } 300 301 private static void addAllUserKeys(long userSerial, HashSet<String> keysOut) { 302 keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial); 303 keysOut.add(USER_FOLDER_ID_PREFIX + userSerial); 304 } 305 306 /** 307 * For each user, if a work folder has not been created, mark it such that the folder will 308 * never get created. 309 */ 310 public static void markExistingUsersForNoFolderCreation(Context context) { 311 UserManagerCompat userManager = UserManagerCompat.getInstance(context); 312 UserHandleCompat myUser = UserHandleCompat.myUserHandle(); 313 314 SharedPreferences prefs = null; 315 for (UserHandleCompat user : userManager.getUserProfiles()) { 316 if (myUser.equals(user)) { 317 continue; 318 } 319 320 if (prefs == null) { 321 prefs = context.getSharedPreferences( 322 LauncherFiles.MANAGED_USER_PREFERENCES_KEY, 323 Context.MODE_PRIVATE); 324 } 325 String folderIdKey = USER_FOLDER_ID_PREFIX + userManager.getSerialNumberForUser(user); 326 if (!prefs.contains(folderIdKey)) { 327 prefs.edit().putLong(folderIdKey, ItemInfo.NO_ID).apply(); 328 } 329 } 330 } 331} 332