b6af1d0efc
Test: Manual Bug:123904290 P2 Remove magic constants from TAPL Bug:130294785 P3 Can no longer long press to open settings in hotseat area Bug:131115553 P2 Enable CacheDataUpdatedTaskTest tests Bug:134712476 P4 Add Duo contact to home screen, icon froze and vanished while over home screen Bug:136278866 P2 Temporary workarounds to make tests pass on Cuttlefish Bug:136829198 P2 Scrolling Overview During Quickswitch causes switch with weird animation Bug:137568159 P4 Refactor LauncherCallback to support multiple clients Bug:137777105 P1 Make clearcut launcher logging feature parity with westworld logging Bug:137851409 P4 Jank during swipe up due to inflation Bug:138620399 P1 Quick switch flicker / artifacts Bug:139016518 P2 [PinnedStackController] Move shelf offset into sysui Bug:139137636 P2 Create memory tests for Launcher Bug:139258979 P2 Switch to the screenshot mode when then system passes ThumbnailData back upon RecentsAnimation cancelation Bug:139259253 P2 Transform multiple app surfaces during app open animation from overview Bug:139439373 P2 Live tile should switch to screenshot before finishing recents animation Bug:139828243 P2 Create a prototype for Overview screenshot actions with a SystemUI plugin Bug:139888225 P2 Convert custom widget into plugins Bug:139913027 P2 [a11y] App title in Widget list shouldn't be actionable by a11y methods. (It would cause Pixel launcher crash.) Bug:139917483 P1 [B1C1][Dec19_QPR][CTS_Verifier_10_r1]Pixel launcher crash observed in Device owner test-> Lock Task UI Bug:139941530 P2 Lab-only Flake: Launcher switches to All Apps after pressing Recents button Bug:140212732 P2 [PO Cable] Strange launcher behavior after restore Bug:140242324 P2 Cache shortcutInfo icons in Launcher Bug:140246642 P4 Add binder tests for launcher interactions Bug:140311911 P2 Flake in Launcher tests: java.lang.AssertionError: Stable state != state: OverviewState, LauncherState Bug:140406263 P2 [a11y] Unable to scroll to the left of the main Home screen to Display google app by Voice access or Switch access. Bug:140539007 P3 After apply the wallpaper on wallpaper picker, the screen will stay on wallpaper picker about 2 second Bug:140626334 P4 Pass wallpaper SC to launcher for animation during launch and swipe up Bug:140635319 P1 [Flaky test] testPromiseIcon_addedFromEligibleSession failing due to NPE on Launcher#getStateManager Bug:140786694 P2 [A11y]No talkback feedback when long pressing on an app at home screen Bug:140935140 P1 Launcher force close observed while accessing app info shortcuts. Bug:141260670 P3 Drag and drop preview doesn't match the destination grid size Bug:141262820 P3 [Grid] icon badge size should scale as the grid size changes Bug:141265063 P2 Long press on homescreen in between icons doesn't bring up home settings Bug:141275518 P2 Test WellbeingTests.testPauseAppFromOverview flakes on Cuttlefish Bug:141315387 P1 [Failing test] 3P launchers + 2 button mode: failed goToOverviewFromHome and goToOverviewFromApp Bug:141376165 P2 Remove static initializations in Launcher Bug:141390432 P1 [Failing test] TaplTestsLauncher3 in 2-button mode Bug:141517004 P1 FallbackRecentsTest.goToOverviewFromHome Failure Bug:141522764 P1 DefaultLayoutProviderTest failures Bug:141523101 P1 TaplTestsQuickstep#testAllAppsFromHome,testAllAppsFromOverview failures Bug:141524555 P1 FallbackRecentsTest.testOverview flake Bug:141576561 P2 Fix FlagOverrideSampleTest inside robolectric test Bug:141576665 P2 AddWorkspaceItemsTaskTest broken Bug:141577881 P2 FileLogTest broken Bug:141579810 P1 ViewInflationDuringSwipeUp test failures Bug:141580748 P2 FallbackRecentsTest#testOverview failure Bug:141697444 P1 TaplTestsLauncher3#testWidgets test failure Bug:141770616 P2 Flake: Can't find a launcher object; selector: BySelector [RES='\Qcom.google.android.apps.nexuslauncher:id/deep_shortcuts_container\E'] (visible state: AllApps), Bug:141772190 P2 Flake: Context menu is still visible afterswiping up to home Bug:141864547 P2 calendar-stable sometimes fails dialog dismissal on cuttlefish Bug:141886704 P2 Use app targets to determine input consumer instead of launcher state Bug:141934188 P2 Automation Test for adding widget automatically Bug:141939911 P3 Clean up SwipeDetector Bug:141986013 P2 Update all apps fade interpolators Bug:142068081 P1 Quick Switch touch swipe dropped, flicker ensues Bug:142120338 P2 Gmail app (work profile) icon not drawing Bug:142148773 P2 grid size should not affect widget padding in WidgetsRecyclerView Bug:142351228 P1 public void testSwipeUp_with_list_widgets() is failing for merge CL Bug:79868152 P3 Fade out caret with workspace and back in when in Overview Bug:136282913 P1 Swipe up from Assistant Fulfillment Card Jank Bug:138473688 P4 Home settings can't be dismissed by swipe up Bug:140252765 P2 Audit Gesture Nav tests (including Back and Quick Switch) Bug:141568904 P1 NPE: Pixel launcher crash is observed when adding Directions widget Bug:142514365 P1 Failing test AddWidgetTest.testDragIcon Bug:142803200 P1 Broken binder tests Change-Id: I811537cc8d406f0acd9fa45daddae4da79ffff12
303 lines
12 KiB
Java
303 lines
12 KiB
Java
/*
|
|
* Copyright (C) 2018 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.icons.cache;
|
|
|
|
import android.content.ComponentName;
|
|
import android.content.pm.ApplicationInfo;
|
|
import android.content.pm.PackageInfo;
|
|
import android.content.pm.PackageManager;
|
|
import android.database.Cursor;
|
|
import android.database.sqlite.SQLiteException;
|
|
import android.os.SystemClock;
|
|
import android.os.UserHandle;
|
|
import android.text.TextUtils;
|
|
import android.util.Log;
|
|
import android.util.SparseBooleanArray;
|
|
|
|
import com.android.launcher3.icons.cache.BaseIconCache.IconDB;
|
|
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map.Entry;
|
|
import java.util.Set;
|
|
import java.util.Stack;
|
|
|
|
/**
|
|
* Utility class to handle updating the Icon cache
|
|
*/
|
|
public class IconCacheUpdateHandler {
|
|
|
|
private static final String TAG = "IconCacheUpdateHandler";
|
|
|
|
/**
|
|
* In this mode, all invalid icons are marked as to-be-deleted in {@link #mItemsToDelete}.
|
|
* This mode is used for the first run.
|
|
*/
|
|
private static final boolean MODE_SET_INVALID_ITEMS = true;
|
|
|
|
/**
|
|
* In this mode, any valid icon is removed from {@link #mItemsToDelete}. This is used for all
|
|
* subsequent runs, which essentially acts as set-union of all valid items.
|
|
*/
|
|
private static final boolean MODE_CLEAR_VALID_ITEMS = false;
|
|
|
|
private static final Object ICON_UPDATE_TOKEN = new Object();
|
|
|
|
private final HashMap<String, PackageInfo> mPkgInfoMap;
|
|
private final BaseIconCache mIconCache;
|
|
|
|
private final HashMap<UserHandle, Set<String>> mPackagesToIgnore = new HashMap<>();
|
|
|
|
private final SparseBooleanArray mItemsToDelete = new SparseBooleanArray();
|
|
private boolean mFilterMode = MODE_SET_INVALID_ITEMS;
|
|
|
|
IconCacheUpdateHandler(BaseIconCache cache) {
|
|
mIconCache = cache;
|
|
|
|
mPkgInfoMap = new HashMap<>();
|
|
|
|
// Remove all active icon update tasks.
|
|
mIconCache.mWorkerHandler.removeCallbacksAndMessages(ICON_UPDATE_TOKEN);
|
|
|
|
createPackageInfoMap();
|
|
}
|
|
|
|
public void setPackagesToIgnore(UserHandle userHandle, Set<String> packages) {
|
|
mPackagesToIgnore.put(userHandle, packages);
|
|
}
|
|
|
|
private void createPackageInfoMap() {
|
|
PackageManager pm = mIconCache.mPackageManager;
|
|
for (PackageInfo info :
|
|
pm.getInstalledPackages(PackageManager.MATCH_UNINSTALLED_PACKAGES)) {
|
|
mPkgInfoMap.put(info.packageName, info);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
|
|
* the DB and are updated.
|
|
* @return The set of packages for which icons have updated.
|
|
*/
|
|
public <T> void updateIcons(List<T> apps, CachingLogic<T> cachingLogic,
|
|
OnUpdateCallback onUpdateCallback) {
|
|
// Filter the list per user
|
|
HashMap<UserHandle, HashMap<ComponentName, T>> userComponentMap = new HashMap<>();
|
|
int count = apps.size();
|
|
for (int i = 0; i < count; i++) {
|
|
T app = apps.get(i);
|
|
UserHandle userHandle = cachingLogic.getUser(app);
|
|
HashMap<ComponentName, T> componentMap = userComponentMap.get(userHandle);
|
|
if (componentMap == null) {
|
|
componentMap = new HashMap<>();
|
|
userComponentMap.put(userHandle, componentMap);
|
|
}
|
|
componentMap.put(cachingLogic.getComponent(app), app);
|
|
}
|
|
|
|
for (Entry<UserHandle, HashMap<ComponentName, T>> entry : userComponentMap.entrySet()) {
|
|
updateIconsPerUser(entry.getKey(), entry.getValue(), cachingLogic, onUpdateCallback);
|
|
}
|
|
|
|
// From now on, clear every valid item from the global valid map.
|
|
mFilterMode = MODE_CLEAR_VALID_ITEMS;
|
|
}
|
|
|
|
/**
|
|
* Updates the persistent DB, such that only entries corresponding to {@param apps} remain in
|
|
* the DB and are updated.
|
|
* @return The set of packages for which icons have updated.
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private <T> void updateIconsPerUser(UserHandle user, HashMap<ComponentName, T> componentMap,
|
|
CachingLogic<T> cachingLogic, OnUpdateCallback onUpdateCallback) {
|
|
Set<String> ignorePackages = mPackagesToIgnore.get(user);
|
|
if (ignorePackages == null) {
|
|
ignorePackages = Collections.emptySet();
|
|
}
|
|
long userSerial = mIconCache.getSerialNumberForUser(user);
|
|
|
|
Stack<T> appsToUpdate = new Stack<>();
|
|
|
|
try (Cursor c = mIconCache.mIconDb.query(
|
|
new String[]{IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT,
|
|
IconDB.COLUMN_LAST_UPDATED, IconDB.COLUMN_VERSION,
|
|
IconDB.COLUMN_SYSTEM_STATE},
|
|
IconDB.COLUMN_USER + " = ? ",
|
|
new String[]{Long.toString(userSerial)})) {
|
|
|
|
final int indexComponent = c.getColumnIndex(IconDB.COLUMN_COMPONENT);
|
|
final int indexLastUpdate = c.getColumnIndex(IconDB.COLUMN_LAST_UPDATED);
|
|
final int indexVersion = c.getColumnIndex(IconDB.COLUMN_VERSION);
|
|
final int rowIndex = c.getColumnIndex(IconDB.COLUMN_ROWID);
|
|
final int systemStateIndex = c.getColumnIndex(IconDB.COLUMN_SYSTEM_STATE);
|
|
|
|
while (c.moveToNext()) {
|
|
String cn = c.getString(indexComponent);
|
|
ComponentName component = ComponentName.unflattenFromString(cn);
|
|
PackageInfo info = mPkgInfoMap.get(component.getPackageName());
|
|
|
|
int rowId = c.getInt(rowIndex);
|
|
if (info == null) {
|
|
if (!ignorePackages.contains(component.getPackageName())) {
|
|
|
|
if (mFilterMode == MODE_SET_INVALID_ITEMS) {
|
|
mIconCache.remove(component, user);
|
|
mItemsToDelete.put(rowId, true);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
if ((info.applicationInfo.flags & ApplicationInfo.FLAG_IS_DATA_ONLY) != 0) {
|
|
// Application is not present
|
|
continue;
|
|
}
|
|
|
|
long updateTime = c.getLong(indexLastUpdate);
|
|
int version = c.getInt(indexVersion);
|
|
T app = componentMap.remove(component);
|
|
if (version == info.versionCode && updateTime == info.lastUpdateTime
|
|
&& TextUtils.equals(c.getString(systemStateIndex),
|
|
mIconCache.getIconSystemState(info.packageName))) {
|
|
|
|
if (mFilterMode == MODE_CLEAR_VALID_ITEMS) {
|
|
mItemsToDelete.put(rowId, false);
|
|
}
|
|
continue;
|
|
}
|
|
if (app == null) {
|
|
if (mFilterMode == MODE_SET_INVALID_ITEMS) {
|
|
mIconCache.remove(component, user);
|
|
mItemsToDelete.put(rowId, true);
|
|
}
|
|
} else {
|
|
appsToUpdate.add(app);
|
|
}
|
|
}
|
|
} catch (SQLiteException e) {
|
|
Log.d(TAG, "Error reading icon cache", e);
|
|
// Continue updating whatever we have read so far
|
|
}
|
|
|
|
// Insert remaining apps.
|
|
if (!componentMap.isEmpty() || !appsToUpdate.isEmpty()) {
|
|
Stack<T> appsToAdd = new Stack<>();
|
|
appsToAdd.addAll(componentMap.values());
|
|
new SerializedIconUpdateTask(userSerial, user, appsToAdd, appsToUpdate, cachingLogic,
|
|
onUpdateCallback).scheduleNext();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Commits all updates as part of the update handler to disk. Not more calls should be made
|
|
* to this class after this.
|
|
*/
|
|
public void finish() {
|
|
// Commit all deletes
|
|
int deleteCount = 0;
|
|
StringBuilder queryBuilder = new StringBuilder()
|
|
.append(IconDB.COLUMN_ROWID)
|
|
.append(" IN (");
|
|
|
|
int count = mItemsToDelete.size();
|
|
for (int i = 0; i < count; i++) {
|
|
if (mItemsToDelete.valueAt(i)) {
|
|
if (deleteCount > 0) {
|
|
queryBuilder.append(", ");
|
|
}
|
|
queryBuilder.append(mItemsToDelete.keyAt(i));
|
|
deleteCount++;
|
|
}
|
|
}
|
|
queryBuilder.append(')');
|
|
|
|
if (deleteCount > 0) {
|
|
mIconCache.mIconDb.delete(queryBuilder.toString(), null);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* A runnable that updates invalid icons and adds missing icons in the DB for the provided
|
|
* LauncherActivityInfo list. Items are updated/added one at a time, so that the
|
|
* worker thread doesn't get blocked.
|
|
*/
|
|
private class SerializedIconUpdateTask<T> implements Runnable {
|
|
private final long mUserSerial;
|
|
private final UserHandle mUserHandle;
|
|
private final Stack<T> mAppsToAdd;
|
|
private final Stack<T> mAppsToUpdate;
|
|
private final CachingLogic<T> mCachingLogic;
|
|
private final HashSet<String> mUpdatedPackages = new HashSet<>();
|
|
private final OnUpdateCallback mOnUpdateCallback;
|
|
|
|
SerializedIconUpdateTask(long userSerial, UserHandle userHandle,
|
|
Stack<T> appsToAdd, Stack<T> appsToUpdate, CachingLogic<T> cachingLogic,
|
|
OnUpdateCallback onUpdateCallback) {
|
|
mUserHandle = userHandle;
|
|
mUserSerial = userSerial;
|
|
mAppsToAdd = appsToAdd;
|
|
mAppsToUpdate = appsToUpdate;
|
|
mCachingLogic = cachingLogic;
|
|
mOnUpdateCallback = onUpdateCallback;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
if (!mAppsToUpdate.isEmpty()) {
|
|
T app = mAppsToUpdate.pop();
|
|
String pkg = mCachingLogic.getComponent(app).getPackageName();
|
|
PackageInfo info = mPkgInfoMap.get(pkg);
|
|
mIconCache.addIconToDBAndMemCache(
|
|
app, mCachingLogic, info, mUserSerial, true /*replace existing*/);
|
|
mUpdatedPackages.add(pkg);
|
|
|
|
if (mAppsToUpdate.isEmpty() && !mUpdatedPackages.isEmpty()) {
|
|
// No more app to update. Notify callback.
|
|
mOnUpdateCallback.onPackageIconsUpdated(mUpdatedPackages, mUserHandle);
|
|
}
|
|
|
|
// Let it run one more time.
|
|
scheduleNext();
|
|
} else if (!mAppsToAdd.isEmpty()) {
|
|
T app = mAppsToAdd.pop();
|
|
PackageInfo info = mPkgInfoMap.get(mCachingLogic.getComponent(app).getPackageName());
|
|
// We do not check the mPkgInfoMap when generating the mAppsToAdd. Although every
|
|
// app should have package info, this is not guaranteed by the api
|
|
if (info != null) {
|
|
mIconCache.addIconToDBAndMemCache(app, mCachingLogic, info,
|
|
mUserSerial, false /*replace existing*/);
|
|
}
|
|
|
|
if (!mAppsToAdd.isEmpty()) {
|
|
scheduleNext();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void scheduleNext() {
|
|
mIconCache.mWorkerHandler.postAtTime(this, ICON_UPDATE_TOKEN,
|
|
SystemClock.uptimeMillis() + 1);
|
|
}
|
|
}
|
|
|
|
public interface OnUpdateCallback {
|
|
|
|
void onPackageIconsUpdated(HashSet<String> updatedPackages, UserHandle user);
|
|
}
|
|
}
|