Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 752848a3 authored by Sunny Goyal's avatar Sunny Goyal
Browse files

Refactoring package tracking in managed profile heuristic into a

separate class

Change-Id: I4f346422e5c7f94f8559942e21aa01b5c96cd8be
parent cc8f9e0a
Loading
Loading
Loading
Loading
+188 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.launcher3.util;

import android.content.Context;
import android.content.SharedPreferences;

import com.android.launcher3.Utilities;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Utility class to track list of installed packages. It persists the list so that apps
 * installed/uninstalled while Launcher was dead can also be handled properly.
 */
public abstract class CachedPackageTracker implements OnAppsChangedCallbackCompat {

    protected static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";

    protected final SharedPreferences mPrefs;
    protected final UserManagerCompat mUserManager;
    protected final LauncherAppsCompat mLauncherApps;

    public CachedPackageTracker(Context context, String preferenceFileName) {
        mPrefs = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE);
        mUserManager = UserManagerCompat.getInstance(context);
        mLauncherApps = LauncherAppsCompat.getInstance(context);
    }

    /**
     * Checks the list of user apps, and generates package event accordingly.
     * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved}
     */
    public void processUserApps(List<LauncherActivityInfoCompat> apps, UserHandleCompat user) {
        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
        HashSet<String> oldPackageSet = new HashSet<>();
        final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey);

        HashSet<String> packagesRemoved = new HashSet<>(oldPackageSet);
        HashSet<String> newPackageSet = new HashSet<>();
        ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>();

        for (LauncherActivityInfoCompat info : apps) {
            String packageName = info.getComponentName().getPackageName();
            newPackageSet.add(packageName);
            packagesRemoved.remove(packageName);

            if (!oldPackageSet.contains(packageName)) {
                oldPackageSet.add(packageName);
                packagesAdded.add(new LauncherActivityInstallInfo(
                        info, info.getFirstInstallTime()));
            }
        }

        if (!packagesAdded.isEmpty() || !packagesRemoved.isEmpty()) {
            mPrefs.edit().putStringSet(prefKey, newPackageSet).apply();

            if (!packagesAdded.isEmpty()) {
                Collections.sort(packagesAdded);
                onLauncherAppsAdded(packagesAdded, user, userAppsExisted);
            }

            if (!packagesRemoved.isEmpty()) {
                for (String pkg : packagesRemoved) {
                    onLauncherPackageRemoved(pkg, user);
                }
            }
        }
    }

    /**
     * Reads the list of user apps which have already been processed.
     * @return false if the list didn't exist, true otherwise
     */
    private boolean getUserApps(HashSet<String> outExistingApps, String prefKey) {
        Set<String> userApps = mPrefs.getStringSet(prefKey, null);
        if (userApps == null) {
            return false;
        } else {
            outExistingApps.addAll(userApps);
            return true;
        }
    }

    @Override
    public void onPackageRemoved(String packageName, UserHandleCompat user) {
        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
        HashSet<String> packageSet = new HashSet<>();
        if (getUserApps(packageSet, prefKey) && packageSet.remove(packageName)) {
            mPrefs.edit().putStringSet(prefKey, packageSet).apply();
        }

        onLauncherPackageRemoved(packageName, user);
    }

    @Override
    public void onPackageAdded(String packageName, UserHandleCompat user) {
        String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
        HashSet<String> packageSet = new HashSet<>();
        final boolean userAppsExisted = getUserApps(packageSet, prefKey);
        if (!packageSet.contains(packageName)) {
            List<LauncherActivityInfoCompat> activities =
                    mLauncherApps.getActivityList(packageName, user);
            if (!activities.isEmpty()) {
                LauncherActivityInfoCompat activityInfo = activities.get(0);

                packageSet.add(packageName);
                mPrefs.edit().putStringSet(prefKey, packageSet).apply();
                onLauncherAppsAdded(Arrays.asList(
                        new LauncherActivityInstallInfo(activityInfo, System.currentTimeMillis())),
                        user, userAppsExisted);
            }
        }
    }

    @Override
    public void onPackageChanged(String packageName, UserHandleCompat user) { }

    @Override
    public void onPackagesAvailable(
            String[] packageNames, UserHandleCompat user, boolean replacing) { }

    @Override
    public void onPackagesUnavailable(
            String[] packageNames, UserHandleCompat user, boolean replacing) { }

    @Override
    public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) { }

    @Override
    public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) { }

    /**
     * Called when new launcher apps are added.
     * @param apps list of newly added activities. Only one entry per package is sent.
     * @param user the user for this event. All activities in {@param apps} will belong to
     *             the same user.
     * @param userAppsExisted false if the list was processed for the first time, like in case
     *                        when Launcher was newly installed or a new user was added.
     */
    protected abstract void onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps,
            UserHandleCompat user, boolean userAppsExisted);

    /**
     * Called when apps are removed from the system.
     */
    protected abstract void onLauncherPackageRemoved(String packageName, UserHandleCompat user);

    protected static class LauncherActivityInstallInfo
            implements Comparable<LauncherActivityInstallInfo> {
        public final LauncherActivityInfoCompat info;
        public final long installTime;

        public LauncherActivityInstallInfo(LauncherActivityInfoCompat info, long installTime) {
            this.info = info;
            this.installTime = installTime;
        }

        @Override
        public int compareTo(LauncherActivityInstallInfo another) {
            return Utilities.longCompare(installTime, another.installTime);
        }
    }
}
+90 −195
Original line number Diff line number Diff line
@@ -16,14 +16,8 @@

package com.android.launcher3.util;

import android.annotation.TargetApi;
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.os.Build;
import android.util.Log;

import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
@@ -35,27 +29,19 @@ 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.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Handles addition of app shortcuts for managed profiles.
 * Methods of class should only be called on {@link LauncherModel#sWorkerThread}.
 */
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class ManagedProfileHeuristic {

    private static final String TAG = "ManagedProfileHeuristic";

    /**
     * Maintain a set of packages installed per user.
     */
@@ -76,118 +62,100 @@ public class ManagedProfileHeuristic {
    }

    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<ShortcutInfo> mHomescreenApps;
    private ArrayList<ShortcutInfo> mWorkFolderApps;
    private HashMap<ShortcutInfo, Long> mShortcutToInstallTimeMap;
    private final UserHandleCompat mUser;

    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);
    public void processPackageRemoved(String[] packages) {
        Preconditions.assertWorkerThread();
        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
        for (String pkg : packages) {
            handler.onPackageRemoved(pkg, mUser);
        }
    }

    private void initVars() {
        mHomescreenApps = new ArrayList<>();
        mWorkFolderApps = new ArrayList<>();
        mShortcutToInstallTimeMap = new HashMap<>();
    public void processPackageAdd(String[] packages) {
        Preconditions.assertWorkerThread();
        ManagedProfilePackageHandler handler = new ManagedProfilePackageHandler();
        for (String pkg : packages) {
            handler.onPackageAdded(pkg, mUser);
        }
    }

    /**
     * Checks the list of user apps and adds icons for newly installed apps on the homescreen or
     * workfolder.
     */
    public void processUserApps(List<LauncherActivityInfoCompat> apps) {
        initVars();
        Preconditions.assertWorkerThread();
        new ManagedProfilePackageHandler().processUserApps(apps, mUser);
    }

        HashSet<String> packageSet = new HashSet<>();
        final boolean userAppsExisted = getUserApps(packageSet);
    private class ManagedProfilePackageHandler extends CachedPackageTracker {

        boolean newPackageAdded = false;
        for (LauncherActivityInfoCompat info : apps) {
            String packageName = info.getComponentName().getPackageName();
            if (!packageSet.contains(packageName)) {
                packageSet.add(packageName);
                newPackageAdded = true;
                markForAddition(info, info.getFirstInstallTime());
        private ManagedProfilePackageHandler() {
            super(mContext, LauncherFiles.MANAGED_USER_PREFERENCES_KEY);
        }

        protected void onLauncherAppsAdded(
                List<LauncherActivityInstallInfo> apps, UserHandleCompat user, boolean userAppsExisted) {
            ArrayList<ShortcutInfo> workFolderApps = new ArrayList<>();
            ArrayList<ShortcutInfo> homescreenApps = new ArrayList<>();

            int count = apps.size();
            long folderCreationTime =
                    mUserManager.getUserCreationTime(user) + AUTO_ADD_TO_FOLDER_DURATION;

            for (int i = 0; i < count; i++) {
                LauncherActivityInstallInfo info = apps.get(i);

                ShortcutInfo si = ShortcutInfo.fromActivityInfo(info.info, mContext);
                ((info.installTime <= folderCreationTime) ? workFolderApps : homescreenApps).add(si);
            }

        if (newPackageAdded) {
            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
            finalizeWorkFolder(user, workFolderApps, homescreenApps);

            // Do not add shortcuts on the homescreen for the first time. This prevents the launcher
            // getting filled with the managed user apps, when it start with a fresh DB (or after
            // a very long time).
            finalizeAdditions(userAppsExisted);
        }
            if (userAppsExisted && !homescreenApps.isEmpty()) {
                mModel.addAndBindAddedWorkspaceItems(mContext, homescreenApps);
            }

    private void markForAddition(LauncherActivityInfoCompat info, long installTime) {
        ArrayList<ShortcutInfo> targetList =
                (installTime <= mUserCreationTime + AUTO_ADD_TO_FOLDER_DURATION) ?
                        mWorkFolderApps : mHomescreenApps;
        ShortcutInfo si = ShortcutInfo.fromActivityInfo(info, mContext);
        mShortcutToInstallTimeMap.put(si, installTime);
        targetList.add(si);
        }

    private void sortList(ArrayList<ShortcutInfo> infos) {
        Collections.sort(infos, new Comparator<ShortcutInfo>() {

        @Override
            public int compare(ShortcutInfo lhs, ShortcutInfo rhs) {
                Long lhsTime = mShortcutToInstallTimeMap.get(lhs);
                Long rhsTime = mShortcutToInstallTimeMap.get(rhs);
                return Utilities.longCompare(lhsTime == null ? 0 : lhsTime,
                        rhsTime == null ? 0 : rhsTime);
            }
        });
        protected void onLauncherPackageRemoved(String packageName, UserHandleCompat user) {
        }

        /**
         * Adds and binds shortcuts marked to be added to the work folder.
         */
    private void finalizeWorkFolder() {
        if (mWorkFolderApps.isEmpty()) {
        private void finalizeWorkFolder(
                UserHandleCompat user, final ArrayList<ShortcutInfo> workFolderApps,
                ArrayList<ShortcutInfo> homescreenApps) {
            if (workFolderApps.isEmpty()) {
                return;
            }
        sortList(mWorkFolderApps);

            // Try to get a work folder.
        String folderIdKey = USER_FOLDER_ID_PREFIX + mUserSerial;
            String folderIdKey = USER_FOLDER_ID_PREFIX + mUserManager.getSerialNumberForUser(user);
            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);
                    homescreenApps.addAll(0, workFolderApps);
                    return;
                }
            saveWorkFolderShortcuts(folderId, workFolder.contents.size());
                saveWorkFolderShortcuts(folderId, workFolder.contents.size(), workFolderApps);

            final ArrayList<ShortcutInfo> 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 (ShortcutInfo info : shortcuts) {
                        for (ShortcutInfo info : workFolderApps) {
                            workFolder.add(info, false);
                        }
                    }
@@ -199,7 +167,7 @@ public class ManagedProfileHeuristic {
                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 (ShortcutInfo info : mWorkFolderApps) {
                for (ShortcutInfo info : workFolderApps) {
                    workFolder.add(info, false);
                }

@@ -207,97 +175,24 @@ public class ManagedProfileHeuristic {
                ArrayList<ItemInfo> itemList = new ArrayList<ItemInfo>(1);
                itemList.add(workFolder);
                mModel.addAndBindAddedWorkspaceItems(mContext, itemList);
            mPrefs.edit().putLong(USER_FOLDER_ID_PREFIX + mUserSerial, workFolder.id).apply();
                mPrefs.edit().putLong(folderIdKey, workFolder.id).apply();

            saveWorkFolderShortcuts(workFolder.id, 0);
                saveWorkFolderShortcuts(workFolder.id, 0, workFolderApps);
            }
        }
    }

    /**
     * Add work folder shortcuts to the DB.
     */
    private void saveWorkFolderShortcuts(long workFolderId, int startingRank) {
        for (ItemInfo info : mWorkFolderApps) {
    private void saveWorkFolderShortcuts(
            long workFolderId, int startingRank, ArrayList<ShortcutInfo> workFolderApps) {
        for (ItemInfo info : workFolderApps) {
            info.rank = startingRank++;
            LauncherModel.addItemToDatabase(mContext, info, workFolderId, 0, 0, 0);
        }
    }

    /**
     * Adds and binds all shortcuts marked for addition.
     */
    private void finalizeAdditions(boolean addHomeScreenShortcuts) {
        finalizeWorkFolder();

        if (addHomeScreenShortcuts && !mHomescreenApps.isEmpty()) {
            sortList(mHomescreenApps);
            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) {
        initVars();
        HashSet<String> packageSet = new HashSet<>();
        final boolean userAppsExisted = getUserApps(packageSet);

        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<LauncherActivityInfoCompat> activities =
                        launcherApps.getActivityList(packageName, mUser);
                if (!activities.isEmpty()) {
                    markForAddition(activities.get(0), installTime);
                }
            }
        }

        if (newPackageAdded) {
            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
            finalizeAdditions(userAppsExisted);
        }
    }

    /**
     * Updates the list of installed packages for the user.
     */
    public void processPackageRemoved(String[] packages) {
        HashSet<String> packageSet = new HashSet<String>();
        getUserApps(packageSet);
        boolean packageRemoved = false;

        for (String packageName : packages) {
            if (packageSet.remove(packageName)) {
                packageRemoved = true;
            }
        }

        if (packageRemoved) {
            mPrefs.edit().putStringSet(mPackageSetKey, packageSet).apply();
        }
    }

    /**
     * Reads the list of user apps which have already been processed.
     * @return false if the list didn't exist, true otherwise
     */
    private boolean getUserApps(HashSet<String> outExistingApps) {
        Set<String> userApps = mPrefs.getStringSet(mPackageSetKey, null);
        if (userApps == null) {
            return false;
        } else {
            outExistingApps.addAll(userApps);
            return true;
        }
    }

    /**
     * Verifies that entries corresponding to {@param users} exist and removes all invalid entries.