1 /*
2  * Copyright (C) 2016 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.UserHandle;
23 
24 import com.android.launcher3.Utilities;
25 import com.android.launcher3.compat.LauncherAppsCompat;
26 import com.android.launcher3.compat.LauncherAppsCompat.OnAppsChangedCallbackCompat;
27 import com.android.launcher3.compat.UserManagerCompat;
28 
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Set;
35 
36 /**
37  * Utility class to track list of installed packages. It persists the list so that apps
38  * installed/uninstalled while Launcher was dead can also be handled properly.
39  */
40 public abstract class CachedPackageTracker implements OnAppsChangedCallbackCompat {
41 
42     protected static final String INSTALLED_PACKAGES_PREFIX = "installed_packages_for_user_";
43 
44     protected final SharedPreferences mPrefs;
45     protected final UserManagerCompat mUserManager;
46     protected final LauncherAppsCompat mLauncherApps;
47 
CachedPackageTracker(Context context, String preferenceFileName)48     public CachedPackageTracker(Context context, String preferenceFileName) {
49         mPrefs = context.getSharedPreferences(preferenceFileName, Context.MODE_PRIVATE);
50         mUserManager = UserManagerCompat.getInstance(context);
51         mLauncherApps = LauncherAppsCompat.getInstance(context);
52     }
53 
54     /**
55      * Checks the list of user apps, and generates package event accordingly.
56      * {@see #onLauncherAppsAdded}, {@see #onLauncherPackageRemoved}
57      */
processUserApps(List<LauncherActivityInfo> apps, UserHandle user)58     public void processUserApps(List<LauncherActivityInfo> apps, UserHandle user) {
59         String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
60         HashSet<String> oldPackageSet = new HashSet<>();
61         final boolean userAppsExisted = getUserApps(oldPackageSet, prefKey);
62 
63         HashSet<String> packagesRemoved = new HashSet<>(oldPackageSet);
64         HashSet<String> newPackageSet = new HashSet<>();
65         ArrayList<LauncherActivityInstallInfo> packagesAdded = new ArrayList<>();
66 
67         for (LauncherActivityInfo info : apps) {
68             String packageName = info.getComponentName().getPackageName();
69             newPackageSet.add(packageName);
70             packagesRemoved.remove(packageName);
71 
72             if (!oldPackageSet.contains(packageName)) {
73                 oldPackageSet.add(packageName);
74                 packagesAdded.add(new LauncherActivityInstallInfo(
75                         info, info.getFirstInstallTime()));
76             }
77         }
78 
79         if (!packagesAdded.isEmpty() || !packagesRemoved.isEmpty()) {
80             mPrefs.edit().putStringSet(prefKey, newPackageSet).apply();
81 
82             if (!packagesAdded.isEmpty()) {
83                 Collections.sort(packagesAdded);
84                 onLauncherAppsAdded(packagesAdded, user, userAppsExisted);
85             }
86 
87             if (!packagesRemoved.isEmpty()) {
88                 for (String pkg : packagesRemoved) {
89                     onLauncherPackageRemoved(pkg, user);
90                 }
91             }
92         }
93     }
94 
95     /**
96      * Reads the list of user apps which have already been processed.
97      * @return false if the list didn't exist, true otherwise
98      */
getUserApps(HashSet<String> outExistingApps, String prefKey)99     private boolean getUserApps(HashSet<String> outExistingApps, String prefKey) {
100         Set<String> userApps = mPrefs.getStringSet(prefKey, null);
101         if (userApps == null) {
102             return false;
103         } else {
104             outExistingApps.addAll(userApps);
105             return true;
106         }
107     }
108 
109     @Override
onPackageRemoved(String packageName, UserHandle user)110     public void onPackageRemoved(String packageName, UserHandle user) {
111         String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
112         HashSet<String> packageSet = new HashSet<>();
113         if (getUserApps(packageSet, prefKey) && packageSet.remove(packageName)) {
114             mPrefs.edit().putStringSet(prefKey, packageSet).apply();
115         }
116 
117         onLauncherPackageRemoved(packageName, user);
118     }
119 
120     @Override
onPackageAdded(String packageName, UserHandle user)121     public void onPackageAdded(String packageName, UserHandle user) {
122         String prefKey = INSTALLED_PACKAGES_PREFIX + mUserManager.getSerialNumberForUser(user);
123         HashSet<String> packageSet = new HashSet<>();
124         final boolean userAppsExisted = getUserApps(packageSet, prefKey);
125         if (!packageSet.contains(packageName)) {
126             List<LauncherActivityInfo> activities =
127                     mLauncherApps.getActivityList(packageName, user);
128             if (!activities.isEmpty()) {
129                 LauncherActivityInfo activityInfo = activities.get(0);
130 
131                 packageSet.add(packageName);
132                 mPrefs.edit().putStringSet(prefKey, packageSet).apply();
133                 onLauncherAppsAdded(Arrays.asList(
134                         new LauncherActivityInstallInfo(activityInfo, System.currentTimeMillis())),
135                         user, userAppsExisted);
136             }
137         }
138     }
139 
140     @Override
onPackageChanged(String packageName, UserHandle user)141     public void onPackageChanged(String packageName, UserHandle user) { }
142 
143     @Override
onPackagesAvailable( String[] packageNames, UserHandle user, boolean replacing)144     public void onPackagesAvailable(
145             String[] packageNames, UserHandle user, boolean replacing) { }
146 
147     @Override
onPackagesUnavailable( String[] packageNames, UserHandle user, boolean replacing)148     public void onPackagesUnavailable(
149             String[] packageNames, UserHandle user, boolean replacing) { }
150 
151     @Override
onPackagesSuspended(String[] packageNames, UserHandle user)152     public void onPackagesSuspended(String[] packageNames, UserHandle user) { }
153 
154     @Override
onPackagesUnsuspended(String[] packageNames, UserHandle user)155     public void onPackagesUnsuspended(String[] packageNames, UserHandle user) { }
156 
157     /**
158      * Called when new launcher apps are added.
159      * @param apps list of newly added activities. Only one entry per package is sent.
160      * @param user the user for this event. All activities in {@param apps} will belong to
161      *             the same user.
162      * @param userAppsExisted false if the list was processed for the first time, like in case
163      *                        when Launcher was newly installed or a new user was added.
164      */
onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps, UserHandle user, boolean userAppsExisted)165     protected abstract void onLauncherAppsAdded(List<LauncherActivityInstallInfo> apps,
166             UserHandle user, boolean userAppsExisted);
167 
168     /**
169      * Called when apps are removed from the system.
170      */
onLauncherPackageRemoved(String packageName, UserHandle user)171     protected abstract void onLauncherPackageRemoved(String packageName, UserHandle user);
172 
173     public static class LauncherActivityInstallInfo
174             implements Comparable<LauncherActivityInstallInfo> {
175         public final LauncherActivityInfo info;
176         public final long installTime;
177 
LauncherActivityInstallInfo(LauncherActivityInfo info, long installTime)178         public LauncherActivityInstallInfo(LauncherActivityInfo info, long installTime) {
179             this.info = info;
180             this.installTime = installTime;
181         }
182 
183         @Override
compareTo(LauncherActivityInstallInfo another)184         public int compareTo(LauncherActivityInstallInfo another) {
185             return Utilities.longCompare(installTime, another.installTime);
186         }
187     }
188 }
189