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 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 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 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 94 public void onStop() { 95 mInstaller.unregisterSessionCallback(mCallback); 96 } 97 98 @Override 99 public void onFinishBind() { 100 mBound = true; 101 mWorker.post(this); 102 } 103 104 @Override 105 public void onPause() { 106 mResumed = false; 107 } 108 109 @Override 110 public void onResume() { 111 mResumed = true; 112 mWorker.post(this); 113 } 114 115 @Override 116 public void recordPackageUpdate(String packageName, int state, int progress) { 117 // No op 118 } 119 120 @Override 121 public void run() { 122 // Called on mWorker thread. 123 replayUpdates(null); 124 } 125 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