package com.android.launcher3.util; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; import com.android.launcher3.FolderInfo; import com.android.launcher3.ItemInfo; import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherFiles; import com.android.launcher3.LauncherModel; import com.android.launcher3.MainThreadExecutor; import com.android.launcher3.R; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; /** * Handles addition of app shortcuts for managed profiles. * Methods of class should only be called on {@link LauncherModel#sWorkerThread}. */ public class ManagedProfileHeuristic { private static final String TAG = "ManagedProfileHeuristic"; /** * Maintain a set of packages installed per user. */ private static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_"; private static final String USER_FOLDER_ID_PREFIX = "user_folder_"; /** * Duration (in milliseconds) for which app shortcuts will be added to work folder. */ private static final long AUTO_ADD_TO_FOLDER_DURATION = 8 * 60 * 60 * 1000; public static ManagedProfileHeuristic get(Context context, UserHandleCompat user) { if (Utilities.isLmpOrAbove() && !UserHandleCompat.myUserHandle().equals(user)) { return new ManagedProfileHeuristic(context, user); } return null; } private final Context mContext; private final UserHandleCompat mUser; private final LauncherModel mModel; private final SharedPreferences mPrefs; private final long mUserSerial; private final long mUserCreationTime; private final String mPackageSetKey; private ArrayList mHomescreenApps; private ArrayList mWorkFolderApps; private ManagedProfileHeuristic(Context context, UserHandleCompat user) { mContext = context; mUser = user; mModel = LauncherAppState.getInstance().getModel(); UserManagerCompat userManager = UserManagerCompat.getInstance(context); mUserSerial = userManager.getSerialNumberForUser(user); mUserCreationTime = userManager.getUserCreationTime(user); mPackageSetKey = INSTALLED_PACKAGES_PREFIX + mUserSerial; mPrefs = mContext.getSharedPreferences(LauncherFiles.MANAGED_USER_PREFERENCES_KEY, Context.MODE_PRIVATE); } /** * Checks the list of user apps and adds icons for newly installed apps on the homescreen or * workfolder. */ public void processUserApps(List apps) { mHomescreenApps = new ArrayList(); mWorkFolderApps = new ArrayList(); HashSet packageSet = getPackageSet(); boolean newPackageAdded = false; for (LauncherActivityInfoCompat info : apps) { String packageName = info.getComponentName().getPackageName(); if (!packageSet.contains(packageName)) { packageSet.add(packageName); newPackageAdded = true; try { PackageInfo pkgInfo = mContext.getPackageManager() .getPackageInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES); markForAddition(info, pkgInfo.firstInstallTime); } catch (NameNotFoundException e) { Log.e(TAG, "Unknown package " + packageName, e); } } } if (newPackageAdded) { mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); finalizeAdditions(); } } private void markForAddition(LauncherActivityInfoCompat info, long installTime) { ArrayList targetList = (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ? mWorkFolderApps : mHomescreenApps; targetList.add(ShortcutInfo.fromActivityInfo(info, mContext)); } /** * Adds and binds shortcuts marked to be added to the work folder. */ private void finalizeWorkFolder() { if (mWorkFolderApps.isEmpty()) { return; } // Try to get a work folder. String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial; if (mPrefs.contains(folderIdKey)) { long folderId = mPrefs.getLong(folderIdKey, 0); final FolderInfo workFolder = mModel.findFolderById(folderId); if (workFolder == null || !workFolder.hasOption(FolderInfo.FLAG_WORK_FOLDER)) { // Could not get a work folder. Add all the icons to homescreen. mHomescreenApps.addAll(mWorkFolderApps); return; } saveWorkFolderShortcuts(folderId, workFolder.contents.size()); final ArrayList shortcuts = mWorkFolderApps; // FolderInfo could already be bound. We need to add shortcuts on the UI thread. new MainThreadExecutor().execute(new Runnable() { @Override public void run() { for (ItemInfo info : shortcuts) { workFolder.add((ShortcutInfo) info); } } }); } else { // Create a new folder. final FolderInfo workFolder = new FolderInfo(); workFolder.title = mContext.getText(R.string.work_folder_name); workFolder.setOption(FolderInfo.FLAG_WORK_FOLDER, true, null); // Add all shortcuts before adding it to the UI, as an empty folder might get deleted. for (ItemInfo info : mWorkFolderApps) { workFolder.add((ShortcutInfo) info); } // Add the item to home screen and DB. This also generates an item id synchronously. ArrayList itemList = new ArrayList(1); itemList.add(workFolder); mModel.addAndBindAddedWorkspaceItems(mContext, itemList); mPrefs.edit().putLong(USER_FOLDER_ID_PREFIX + mUserSerial, workFolder.id).apply(); saveWorkFolderShortcuts(workFolder.id, 0); } } /** * Add work folder shortcuts to the DB. */ private void saveWorkFolderShortcuts(long workFolderId, int startingRank) { for (ItemInfo info : mWorkFolderApps) { info.rank = startingRank++; LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0); } } /** * Adds and binds all shortcuts marked for addition. */ private void finalizeAdditions() { finalizeWorkFolder(); if (!mHomescreenApps.isEmpty()) { mModel.addAndBindAddedWorkspaceItems(mContext, mHomescreenApps); } } /** * Updates the list of installed apps and adds any new icons on homescreen or work folder. */ public void processPackageAdd(String[] packages) { mHomescreenApps = new ArrayList(); mWorkFolderApps = new ArrayList(); HashSet packageSet = getPackageSet(); boolean newPackageAdded = false; long installTime = System.currentTimeMillis(); LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mContext); for (String packageName : packages) { if (!packageSet.contains(packageName)) { packageSet.add(packageName); newPackageAdded = true; List activities = launcherApps.getActivityList(packageName, mUser); if (!activities.isEmpty()) { markForAddition(activities.get(0), installTime); } } } if (newPackageAdded) { mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); finalizeAdditions(); } } /** * Updates the list of installed packages for the user. */ public void processPackageRemoved(String[] packages) { HashSet packageSet = getPackageSet(); boolean packageRemoved = false; for (String packageName : packages) { if (packageSet.remove(packageName)) { packageRemoved = true; } } if (packageRemoved) { mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply(); } } @SuppressWarnings("unchecked") private HashSet getPackageSet() { return new HashSet(mPrefs.getStringSet(mPackageSetKey, Collections.EMPTY_SET)); } /** * Verifies that entries corresponding to {@param users} exist and removes all invalid entries. */ public static void processAllUsers(List users, Context context) { if (!Utilities.isLmpOrAbove()) { return; } UserManagerCompat userManager = UserManagerCompat.getInstance(context); HashSet validKeys = new HashSet(); for (UserHandleCompat user : users) { addAllUserKeys(userManager.getSerialNumberForUser(user), validKeys); } SharedPreferences prefs = context.getSharedPreferences( LauncherFiles.MANAGED_USER_PREFERENCES_KEY, Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); for (String key : prefs.getAll().keySet()) { if (!validKeys.contains(key)) { editor.remove(key); } } editor.apply(); } private static void addAllUserKeys(long userSerial, HashSet keysOut) { keysOut.add(INSTALLED_PACKAGES_PREFIX + userSerial); keysOut.add(USER_FOLDER_ID_PREFIX + userSerial); } }