a8c7c0b713
- In some cases, SessionInfo is created without an icon until later on in the install process. This prevents promise icons with default Android logo from appearing. - This will also prevent apps without a launching activity from appearing as a promise icon and then disappearing when the app is installed. Bug: 135633159 Change-Id: I30adde8bf2cf583bffca8ed878f9cc20d6d41a13
282 lines
11 KiB
Java
282 lines
11 KiB
Java
/*
|
|
* Copyright (C) 2014 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.compat;
|
|
|
|
import android.content.Context;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageInstaller;
|
|
import android.content.pm.PackageInstaller.SessionCallback;
|
|
import android.content.pm.PackageInstaller.SessionInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.os.Handler;
|
|
import android.os.UserHandle;
|
|
import android.text.TextUtils;
|
|
import android.util.SparseArray;
|
|
|
|
import com.android.launcher3.SessionCommitReceiver;
|
|
import com.android.launcher3.Utilities;
|
|
import com.android.launcher3.icons.IconCache;
|
|
import com.android.launcher3.LauncherAppState;
|
|
import com.android.launcher3.LauncherModel;
|
|
import com.android.launcher3.config.FeatureFlags;
|
|
import com.android.launcher3.util.IntArray;
|
|
import com.android.launcher3.util.IntSet;
|
|
import com.android.launcher3.util.PackageUserKey;
|
|
import com.android.launcher3.util.Thunk;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
|
|
import static com.android.launcher3.Utilities.getPrefs;
|
|
|
|
public class PackageInstallerCompatVL extends PackageInstallerCompat {
|
|
|
|
private static final boolean DEBUG = false;
|
|
|
|
@Thunk final SparseArray<PackageUserKey> mActiveSessions = new SparseArray<>();
|
|
|
|
@Thunk final PackageInstaller mInstaller;
|
|
private final IconCache mCache;
|
|
private final Handler mWorker;
|
|
private final Context mAppContext;
|
|
private final HashMap<String,Boolean> mSessionVerifiedMap = new HashMap<>();
|
|
private final LauncherAppsCompat mLauncherApps;
|
|
private final IntSet mPromiseIconIds;
|
|
|
|
PackageInstallerCompatVL(Context context) {
|
|
mAppContext = context.getApplicationContext();
|
|
mInstaller = context.getPackageManager().getPackageInstaller();
|
|
mCache = LauncherAppState.getInstance(context).getIconCache();
|
|
mWorker = new Handler(LauncherModel.getWorkerLooper());
|
|
mInstaller.registerSessionCallback(mCallback, mWorker);
|
|
mLauncherApps = LauncherAppsCompat.getInstance(context);
|
|
mPromiseIconIds = IntSet.wrap(IntArray.wrap(Utilities.getIntArrayFromString(
|
|
getPrefs(context).getString(PROMISE_ICON_IDS, ""))));
|
|
|
|
cleanUpPromiseIconIds();
|
|
}
|
|
|
|
private void cleanUpPromiseIconIds() {
|
|
IntArray existingIds = new IntArray();
|
|
for (SessionInfo info : updateAndGetActiveSessionCache().values()) {
|
|
existingIds.add(info.getSessionId());
|
|
}
|
|
IntArray idsToRemove = new IntArray();
|
|
|
|
for (int i = mPromiseIconIds.size() - 1; i >= 0; --i) {
|
|
if (!existingIds.contains(mPromiseIconIds.getArray().get(i))) {
|
|
idsToRemove.add(mPromiseIconIds.getArray().get(i));
|
|
}
|
|
}
|
|
for (int i = idsToRemove.size() - 1; i >= 0; --i) {
|
|
mPromiseIconIds.getArray().removeValue(idsToRemove.get(i));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public HashMap<String, SessionInfo> updateAndGetActiveSessionCache() {
|
|
HashMap<String, SessionInfo> activePackages = new HashMap<>();
|
|
for (SessionInfo info : getAllVerifiedSessions()) {
|
|
addSessionInfoToCache(info, getUserHandle(info));
|
|
if (info.getAppPackageName() != null) {
|
|
activePackages.put(info.getAppPackageName(), info);
|
|
mActiveSessions.put(info.getSessionId(),
|
|
new PackageUserKey(info.getAppPackageName(), getUserHandle(info)));
|
|
}
|
|
}
|
|
return activePackages;
|
|
}
|
|
|
|
public SessionInfo getActiveSessionInfo(UserHandle user, String pkg) {
|
|
for (SessionInfo info : getAllVerifiedSessions()) {
|
|
boolean match = pkg.equals(info.getAppPackageName());
|
|
if (Utilities.ATLEAST_Q && !user.equals(getUserHandle(info))) {
|
|
match = false;
|
|
}
|
|
if (match) {
|
|
return info;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Thunk void addSessionInfoToCache(SessionInfo info, UserHandle user) {
|
|
String packageName = info.getAppPackageName();
|
|
if (packageName != null) {
|
|
mCache.cachePackageInstallInfo(packageName, user, info.getAppIcon(),
|
|
info.getAppLabel());
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onStop() {
|
|
mInstaller.unregisterSessionCallback(mCallback);
|
|
}
|
|
|
|
@Thunk void sendUpdate(PackageInstallInfo info) {
|
|
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
|
|
if (app != null) {
|
|
app.getModel().setPackageState(info);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add a promise app icon to the workspace iff:
|
|
* - The settings for it are enabled
|
|
* - The user installed the app
|
|
* - There is a provided app icon (For apps with no launching activity, no icon is provided).
|
|
*/
|
|
private void tryQueuePromiseAppIcon(SessionInfo sessionInfo) {
|
|
if (Utilities.ATLEAST_OREO && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()
|
|
&& SessionCommitReceiver.isEnabled(mAppContext)
|
|
&& sessionInfo != null
|
|
&& sessionInfo.getInstallReason() == PackageManager.INSTALL_REASON_USER
|
|
&& sessionInfo.getAppIcon() != null
|
|
&& !mPromiseIconIds.contains(sessionInfo.getSessionId())) {
|
|
SessionCommitReceiver.queuePromiseAppIconAddition(mAppContext, sessionInfo);
|
|
mPromiseIconIds.add(sessionInfo.getSessionId());
|
|
updatePromiseIconPrefs();
|
|
}
|
|
}
|
|
|
|
private final SessionCallback mCallback = new SessionCallback() {
|
|
|
|
@Override
|
|
public void onCreated(int sessionId) {
|
|
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
|
|
if (FeatureFlags.LAUNCHER3_PROMISE_APPS_IN_ALL_APPS && sessionInfo != null) {
|
|
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
|
|
if (app != null) {
|
|
app.getModel().onInstallSessionCreated(
|
|
PackageInstallInfo.fromInstallingState(sessionInfo));
|
|
}
|
|
}
|
|
|
|
tryQueuePromiseAppIcon(sessionInfo);
|
|
}
|
|
|
|
@Override
|
|
public void onFinished(int sessionId, boolean success) {
|
|
// For a finished session, we can't get the session info. So use the
|
|
// packageName from our local cache.
|
|
PackageUserKey key = mActiveSessions.get(sessionId);
|
|
mActiveSessions.remove(sessionId);
|
|
|
|
if (key != null && key.mPackageName != null) {
|
|
String packageName = key.mPackageName;
|
|
sendUpdate(PackageInstallInfo.fromState(success ? STATUS_INSTALLED : STATUS_FAILED,
|
|
packageName, key.mUser));
|
|
|
|
if (!success && FeatureFlags.PROMISE_APPS_NEW_INSTALLS.get()) {
|
|
LauncherAppState appState = LauncherAppState.getInstanceNoCreate();
|
|
if (appState != null) {
|
|
LauncherModel model = appState.getModel();
|
|
model.onPackageRemoved(packageName, key.mUser);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onProgressChanged(int sessionId, float progress) {
|
|
SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
|
|
if (session != null && session.getAppPackageName() != null) {
|
|
sendUpdate(PackageInstallInfo.fromInstallingState(session));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onActiveChanged(int sessionId, boolean active) { }
|
|
|
|
@Override
|
|
public void onBadgingChanged(int sessionId) {
|
|
SessionInfo sessionInfo = pushSessionDisplayToLauncher(sessionId);
|
|
if (sessionInfo != null) {
|
|
tryQueuePromiseAppIcon(sessionInfo);
|
|
}
|
|
}
|
|
|
|
private SessionInfo pushSessionDisplayToLauncher(int sessionId) {
|
|
SessionInfo session = verify(mInstaller.getSessionInfo(sessionId));
|
|
if (session != null && session.getAppPackageName() != null) {
|
|
mActiveSessions.put(session.getSessionId(),
|
|
new PackageUserKey(session.getAppPackageName(), getUserHandle(session)));
|
|
addSessionInfoToCache(session, getUserHandle(session));
|
|
LauncherAppState app = LauncherAppState.getInstanceNoCreate();
|
|
if (app != null) {
|
|
app.getModel().updateSessionDisplayInfo(session.getAppPackageName());
|
|
}
|
|
return session;
|
|
}
|
|
return null;
|
|
}
|
|
};
|
|
|
|
private PackageInstaller.SessionInfo verify(PackageInstaller.SessionInfo sessionInfo) {
|
|
if (sessionInfo == null
|
|
|| sessionInfo.getInstallerPackageName() == null
|
|
|| TextUtils.isEmpty(sessionInfo.getAppPackageName())) {
|
|
return null;
|
|
}
|
|
String pkg = sessionInfo.getInstallerPackageName();
|
|
synchronized (mSessionVerifiedMap) {
|
|
if (!mSessionVerifiedMap.containsKey(pkg)) {
|
|
LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(mAppContext);
|
|
boolean hasSystemFlag = launcherApps.getApplicationInfo(pkg,
|
|
ApplicationInfo.FLAG_SYSTEM, getUserHandle(sessionInfo)) != null;
|
|
mSessionVerifiedMap.put(pkg, DEBUG || hasSystemFlag);
|
|
}
|
|
}
|
|
return mSessionVerifiedMap.get(pkg) ? sessionInfo : null;
|
|
}
|
|
|
|
@Override
|
|
public List<SessionInfo> getAllVerifiedSessions() {
|
|
List<SessionInfo> list = new ArrayList<>(Utilities.ATLEAST_Q
|
|
? mLauncherApps.getAllPackageInstallerSessions()
|
|
: mInstaller.getAllSessions());
|
|
Iterator<SessionInfo> it = list.iterator();
|
|
while (it.hasNext()) {
|
|
if (verify(it.next()) == null) {
|
|
it.remove();
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
@Override
|
|
public boolean promiseIconAddedForId(int sessionId) {
|
|
return mPromiseIconIds.contains(sessionId);
|
|
}
|
|
|
|
@Override
|
|
public void removePromiseIconId(int sessionId) {
|
|
if (mPromiseIconIds.contains(sessionId)) {
|
|
mPromiseIconIds.getArray().removeValue(sessionId);
|
|
updatePromiseIconPrefs();
|
|
}
|
|
}
|
|
|
|
private void updatePromiseIconPrefs() {
|
|
getPrefs(mAppContext).edit()
|
|
.putString(PROMISE_ICON_IDS, mPromiseIconIds.getArray().toConcatString())
|
|
.apply();
|
|
}
|
|
}
|