1 /*
2  * Copyright (C) 2014 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.compat;
18 
19 import android.content.Context;
20 import android.content.pm.PackageInstaller;
21 import android.content.pm.PackageInstaller.SessionCallback;
22 import android.content.pm.PackageInstaller.SessionInfo;
23 import android.os.Handler;
24 import android.util.Log;
25 import android.util.SparseArray;
26 
27 import com.android.launcher3.IconCache;
28 import com.android.launcher3.LauncherAppState;
29 
30 import java.util.ArrayList;
31 import java.util.HashSet;
32 
33 public class PackageInstallerCompatVL extends PackageInstallerCompat implements Runnable {
34 
35     private static final String TAG = "PackageInstallerCompatVL";
36     private static final boolean DEBUG = false;
37 
38     // All updates to these sets must happen on the {@link #mWorker} thread.
39     private final SparseArray<SessionInfo> mPendingReplays = new SparseArray<SessionInfo>();
40     private final HashSet<String> mPendingBadgeUpdates = new HashSet<String>();
41 
42     private final PackageInstaller mInstaller;
43     private final IconCache mCache;
44     private final Handler mWorker;
45 
46     private boolean mResumed;
47     private boolean mBound;
48 
PackageInstallerCompatVL(Context context)49     PackageInstallerCompatVL(Context context) {
50         mInstaller = context.getPackageManager().getPackageInstaller();
51         LauncherAppState.setApplicationContext(context.getApplicationContext());
52         mCache = LauncherAppState.getInstance().getIconCache();
53         mWorker = new Handler();
54 
55         mResumed = false;
56         mBound = false;
57 
58         mInstaller.registerSessionCallback(mCallback, mWorker);
59 
60         // On start, send updates for all active sessions
61         mWorker.post(new Runnable() {
62 
63             @Override
64             public void run() {
65                 for (SessionInfo info : mInstaller.getAllSessions()) {
66                     mPendingReplays.append(info.getSessionId(), info);
67                 }
68             }
69         });
70     }
71 
72     @Override
updateAndGetActiveSessionCache()73     public HashSet<String> updateAndGetActiveSessionCache() {
74         HashSet<String> activePackages = new HashSet<String>();
75         UserHandleCompat user = UserHandleCompat.myUserHandle();
76         for (SessionInfo info : mInstaller.getAllSessions()) {
77             addSessionInfoToCahce(info, user);
78             if (info.getAppPackageName() != null) {
79                 activePackages.add(info.getAppPackageName());
80             }
81         }
82         return activePackages;
83     }
84 
addSessionInfoToCahce(SessionInfo info, UserHandleCompat user)85     private void addSessionInfoToCahce(SessionInfo info, UserHandleCompat user) {
86         String packageName = info.getAppPackageName();
87         if (packageName != null) {
88             mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(),
89                     info.getAppLabel());
90         }
91     }
92 
93     @Override
onStop()94     public void onStop() {
95         mInstaller.unregisterSessionCallback(mCallback);
96     }
97 
98     @Override
onFinishBind()99     public void onFinishBind() {
100         mBound = true;
101         mWorker.post(this);
102     }
103 
104     @Override
onPause()105     public void onPause() {
106         mResumed = false;
107     }
108 
109     @Override
onResume()110     public void onResume() {
111         mResumed = true;
112         mWorker.post(this);
113     }
114 
115     @Override
recordPackageUpdate(String packageName, int state, int progress)116     public void recordPackageUpdate(String packageName, int state, int progress) {
117         // No op
118     }
119 
120     @Override
run()121     public void run() {
122         // Called on mWorker thread.
123         replayUpdates(null);
124     }
125 
replayUpdates(PackageInstallInfo newInfo)126     private void replayUpdates(PackageInstallInfo newInfo) {
127         if (DEBUG) Log.d(TAG, "updates resumed");
128         if (!mResumed || !mBound) {
129             // Not yet ready
130             return;
131         }
132         if ((mPendingReplays.size() == 0) && (newInfo == null)) {
133             // Nothing to update
134             return;
135         }
136 
137         LauncherAppState app = LauncherAppState.getInstanceNoCreate();
138         if (app == null) {
139             // Try again later
140             if (DEBUG) Log.d(TAG, "app is null, delaying send");
141             return;
142         }
143 
144         ArrayList<PackageInstallInfo> updates = new ArrayList<PackageInstallInfo>();
145         if ((newInfo != null) && (newInfo.state != STATUS_INSTALLED)) {
146             updates.add(newInfo);
147         }
148         for (int i = mPendingReplays.size() - 1; i >= 0; i--) {
149             SessionInfo session = mPendingReplays.valueAt(i);
150             if (session.getAppPackageName() != null) {
151                 updates.add(new PackageInstallInfo(session.getAppPackageName(),
152                         STATUS_INSTALLING,
153                         (int) (session.getProgress() * 100)));
154             }
155         }
156         mPendingReplays.clear();
157         if (!updates.isEmpty()) {
158             app.setPackageState(updates);
159         }
160 
161         if (!mPendingBadgeUpdates.isEmpty()) {
162             for (String pkg : mPendingBadgeUpdates) {
163                 app.updatePackageBadge(pkg);
164             }
165             mPendingBadgeUpdates.clear();
166         }
167     }
168 
169     private final SessionCallback mCallback = new SessionCallback() {
170 
171         @Override
172         public void onCreated(int sessionId) {
173             pushSessionBadgeToLauncher(sessionId);
174         }
175 
176         @Override
177         public void onFinished(int sessionId, boolean success) {
178             mPendingReplays.remove(sessionId);
179             SessionInfo session = mInstaller.getSessionInfo(sessionId);
180             if ((session != null) && (session.getAppPackageName() != null)) {
181                 mPendingBadgeUpdates.remove(session.getAppPackageName());
182                 // Replay all updates with a one time update for this installed package. No
183                 // need to store this record for future updates, as the app list will get
184                 // refreshed on resume.
185                 replayUpdates(new PackageInstallInfo(session.getAppPackageName(),
186                         success ? STATUS_INSTALLED : STATUS_FAILED, 0));
187             }
188         }
189 
190         @Override
191         public void onProgressChanged(int sessionId, float progress) {
192             SessionInfo session = mInstaller.getSessionInfo(sessionId);
193             if (session != null) {
194                 mPendingReplays.put(sessionId, session);
195                 replayUpdates(null);
196             }
197         }
198 
199         @Override
200         public void onActiveChanged(int sessionId, boolean active) { }
201 
202         @Override
203         public void onBadgingChanged(int sessionId) {
204             pushSessionBadgeToLauncher(sessionId);
205         }
206 
207         private void pushSessionBadgeToLauncher(int sessionId) {
208             SessionInfo session = mInstaller.getSessionInfo(sessionId);
209             if (session != null) {
210                 addSessionInfoToCahce(session, UserHandleCompat.myUserHandle());
211                 if (session.getAppPackageName() != null) {
212                     mPendingBadgeUpdates.add(session.getAppPackageName());
213                 }
214                 mPendingReplays.put(sessionId, session);
215                 replayUpdates(null);
216             }
217         }
218     };
219 }
220