Merge "Keeping icons in disabled state when SD-card is unmounted" into ub-now-queens

This commit is contained in:
Sunny Goyal
2014-10-08 18:10:39 +00:00
committed by Android (Google) Code Review
9 changed files with 171 additions and 71 deletions
+2
View File
@@ -40,6 +40,8 @@
<string name="folder_name"></string>
<!-- Displayed when user selects a shortcut for an app that was uninstalled [CHAR_LIMIT=none]-->
<string name="activity_not_found">App isn\'t installed.</string>
<!-- Displayed when user selects a shortcut for an app that is current not available [CHAR_LIMIT=none]-->
<string name="activity_not_available">App isn\'t available</string>
<!-- SafeMode shortcut error string -->
<string name="safemode_shortcut_error">Downloaded app disabled in Safe mode</string>
<!-- Labels for the tabs in the customize drawer -->
+4 -2
View File
@@ -105,7 +105,7 @@ class AllAppsList {
/**
* Remove the apps for the given apk identified by packageName.
*/
public void removePackage(String packageName, UserHandleCompat user) {
public void removePackage(String packageName, UserHandleCompat user, boolean clearCache) {
final List<AppInfo> data = this.data;
for (int i = data.size() - 1; i >= 0; i--) {
AppInfo info = data.get(i);
@@ -115,7 +115,9 @@ class AllAppsList {
data.remove(i);
}
}
mIconCache.remove(packageName, user);
if (clearCache) {
mIconCache.remove(packageName, user);
}
}
/**
@@ -122,7 +122,7 @@ public class BubbleTextView extends TextView {
LauncherAppState app = LauncherAppState.getInstance();
FastBitmapDrawable iconDrawable = Utilities.createIconDrawable(b);
iconDrawable.setGhostModeEnabled(info.isDisabled);
iconDrawable.setGhostModeEnabled(info.isDisabled != 0);
setCompoundDrawables(null, iconDrawable, null, null);
if (setDefaultPadding) {
+23 -7
View File
@@ -2590,6 +2590,16 @@ public class Launcher extends Activity
// Open shortcut
final ShortcutInfo shortcut = (ShortcutInfo) tag;
if (shortcut.isDisabled != 0) {
int error = R.string.activity_not_available;
if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SAFEMODE) != 0) {
error = R.string.safemode_shortcut_error;
}
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
return;
}
final Intent intent = shortcut.intent;
// Check for special shortcuts
@@ -4766,24 +4776,30 @@ public class Launcher extends Activity
* we only remove specific components from the workspace, where as
* package-removal should clear all items by package name.
*
* @param reason if non-zero, the icons are not permanently removed, rather marked as disabled.
* Implementation of the method from LauncherModel.Callbacks.
*/
@Override
public void bindComponentsRemoved(final ArrayList<String> packageNames,
final ArrayList<AppInfo> appInfos, final UserHandleCompat user) {
final ArrayList<AppInfo> appInfos, final UserHandleCompat user, final int reason) {
Runnable r = new Runnable() {
public void run() {
bindComponentsRemoved(packageNames, appInfos, user);
bindComponentsRemoved(packageNames, appInfos, user, reason);
}
};
if (waitUntilResume(r)) {
return;
}
if (!packageNames.isEmpty()) {
mWorkspace.removeItemsByPackageName(packageNames, user);
}
if (!appInfos.isEmpty()) {
mWorkspace.removeItemsByApplicationInfo(appInfos, user);
if (reason == 0) {
if (!packageNames.isEmpty()) {
mWorkspace.removeItemsByPackageName(packageNames, user);
}
if (!appInfos.isEmpty()) {
mWorkspace.removeItemsByApplicationInfo(appInfos, user);
}
} else {
mWorkspace.disableShortcutsByPackageName(packageNames, user, reason);
}
// Notify the drag controller
+75 -48
View File
@@ -202,7 +202,7 @@ public class LauncherModel extends BroadcastReceiver
public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
public void updatePackageBadge(String packageName);
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user);
ArrayList<AppInfo> appInfos, UserHandleCompat user, int reason);
public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
public void bindSearchablesChanged();
public boolean isAllAppsButtonRank(int rank);
@@ -1981,6 +1981,7 @@ public class LauncherModel extends BroadcastReceiver
long serialNumber = c.getInt(profileIdIndex);
user = mUserManager.getUserForSerialNumber(serialNumber);
int promiseType = c.getInt(restoredIndex);
int disabledState = 0;
if (user == null) {
// User has been deleted remove the item.
itemsToRemove.add(id);
@@ -2054,14 +2055,13 @@ public class LauncherModel extends BroadcastReceiver
itemsToRemove.add(id);
continue;
}
} else if (isSdCardReady) {
// Do not wait for external media load anymore.
// Log the invalid package, and remove it
Launcher.addDumpLog(TAG,
"Invalid package removed: " + cn, true);
itemsToRemove.add(id);
continue;
} else {
} else if (launcherApps.isAppEnabled(
manager, cn.getPackageName(),
PackageManager.GET_UNINSTALLED_PACKAGES)) {
// Package is present but not available.
allowMissingTarget = true;
disabledState = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
} else if (!isSdCardReady) {
// SdCard is not ready yet. Package might get available,
// once it is ready.
Launcher.addDumpLog(TAG, "Invalid package: " + cn
@@ -2074,6 +2074,14 @@ public class LauncherModel extends BroadcastReceiver
pkgs.add(cn.getPackageName());
allowMissingTarget = true;
// Add the icon on the workspace anyway.
} else {
// Do not wait for external media load anymore.
// Log the invalid package, and remove it
Launcher.addDumpLog(TAG,
"Invalid package removed: " + cn, true);
itemsToRemove.add(id);
continue;
}
} else if (cn == null) {
// For shortcuts with no component, keep them as they are
@@ -2131,8 +2139,10 @@ public class LauncherModel extends BroadcastReceiver
info.spanX = 1;
info.spanY = 1;
info.intent.putExtra(ItemInfo.EXTRA_PROFILE, serialNumber);
info.isDisabled = isSafeMode
&& !Utilities.isSystemApp(context, intent);
info.isDisabled = disabledState;
if (isSafeMode && !Utilities.isSystemApp(context, intent)) {
info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SAFEMODE;
}
// check & update map of what's occupied
deleteOnInvalidPlacement.set(false);
@@ -2930,20 +2940,34 @@ public class LauncherModel extends BroadcastReceiver
synchronized (sBgLock) {
final LauncherAppsCompat launcherApps = LauncherAppsCompat
.getInstance(mApp.getContext());
ArrayList<String> packagesRemoved;
final PackageManager manager = context.getPackageManager();
final ArrayList<String> packagesRemoved = new ArrayList<String>();
final ArrayList<String> packagesUnavailable = new ArrayList<String>();
for (Entry<UserHandleCompat, HashSet<String>> entry : sPendingPackages.entrySet()) {
UserHandleCompat user = entry.getKey();
packagesRemoved = new ArrayList<String>();
packagesRemoved.clear();
packagesUnavailable.clear();
for (String pkg : entry.getValue()) {
if (!launcherApps.isPackageEnabledForProfile(pkg, user)) {
Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
packagesRemoved.add(pkg);
boolean packageOnSdcard = launcherApps.isAppEnabled(
manager, pkg, PackageManager.GET_UNINSTALLED_PACKAGES);
if (packageOnSdcard) {
Launcher.addDumpLog(TAG, "Package found on sd-card: " + pkg, true);
packagesUnavailable.add(pkg);
} else {
Launcher.addDumpLog(TAG, "Package not found: " + pkg, true);
packagesRemoved.add(pkg);
}
}
}
if (!packagesRemoved.isEmpty()) {
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_REMOVE,
packagesRemoved.toArray(new String[packagesRemoved.size()]), user));
}
if (!packagesUnavailable.isEmpty()) {
enqueuePackageUpdated(new PackageUpdatedTask(PackageUpdatedTask.OP_UNAVAILABLE,
packagesUnavailable.toArray(new String[packagesUnavailable.size()]), user));
}
}
sPendingPackages.clear();
}
@@ -2991,9 +3015,10 @@ public class LauncherModel extends BroadcastReceiver
break;
case OP_REMOVE:
case OP_UNAVAILABLE:
boolean clearCache = mOp == OP_REMOVE;
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.removePackage " + packages[i]);
mBgAllAppsList.removePackage(packages[i], mUser);
mBgAllAppsList.removePackage(packages[i], mUser, clearCache);
WidgetPreviewLoader.removePackageFromDb(
mApp.getWidgetPreviewCacheDb(), packages[i]);
}
@@ -3041,7 +3066,7 @@ public class LauncherModel extends BroadcastReceiver
ArrayList<ItemInfo> infos =
getItemInfoForComponentName(a.componentName, mUser);
for (ItemInfo i : infos) {
if (isShortcutInfoUpdateable(i)) {
if (i instanceof ShortcutInfo && isShortcutAppTarget((ShortcutInfo) i)) {
ShortcutInfo info = (ShortcutInfo) i;
info.title = a.title.toString();
info.contentDescription = a.contentDescription;
@@ -3064,7 +3089,7 @@ public class LauncherModel extends BroadcastReceiver
if (mOp == OP_ADD || mOp == OP_UPDATE) {
final ArrayList<ShortcutInfo> iconsChanged = new ArrayList<ShortcutInfo>();
HashSet<String> packageSet = new HashSet<String>(Arrays.asList(packages));
// We need to iteration over the items here, so that we can avoid new Bitmap
// We need to iterate over the items here, so that we can avoid new Bitmap
// creation on the UI thread.
synchronized (sBgLock) {
for (ItemInfo info : sBgWorkspaceItems) {
@@ -3099,28 +3124,35 @@ public class LauncherModel extends BroadcastReceiver
final ArrayList<String> removedPackageNames =
new ArrayList<String>();
if (mOp == OP_REMOVE) {
if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE) {
// Mark all packages in the broadcast to be removed
removedPackageNames.addAll(Arrays.asList(packages));
} else if (mOp == OP_UPDATE) {
// Mark disabled packages in the broadcast to be removed
final PackageManager pm = context.getPackageManager();
for (int i=0; i<N; i++) {
if (isPackageDisabled(context, packages[i], mUser)) {
removedPackageNames.add(packages[i]);
}
}
}
// Remove all the components associated with this package
for (String pn : removedPackageNames) {
deletePackageFromDatabase(context, pn, mUser);
}
// Remove all the specific components
for (AppInfo a : removedApps) {
ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
deleteItemsFromDatabase(context, infos);
}
if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) {
final int removeReason;
if (mOp == OP_UNAVAILABLE) {
removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
} else {
// Remove all the components associated with this package
for (String pn : removedPackageNames) {
deletePackageFromDatabase(context, pn, mUser);
}
// Remove all the specific components
for (AppInfo a : removedApps) {
ArrayList<ItemInfo> infos = getItemInfoForComponentName(a.componentName, mUser);
deleteItemsFromDatabase(context, infos);
}
removeReason = 0;
}
// Remove any queued items from the install queue
String spKey = LauncherAppState.getSharedPreferencesKey();
SharedPreferences sp =
@@ -3131,7 +3163,8 @@ public class LauncherModel extends BroadcastReceiver
public void run() {
Callbacks cb = mCallbacks != null ? mCallbacks.get() : null;
if (callbacks == cb && cb != null) {
callbacks.bindComponentsRemoved(removedPackageNames, removedApps, mUser);
callbacks.bindComponentsRemoved(
removedPackageNames, removedApps, mUser, removeReason);
}
}
});
@@ -3381,24 +3414,18 @@ public class LauncherModel extends BroadcastReceiver
return filterItemInfos(sBgItemsIdMap.values(), filter);
}
public static boolean isShortcutInfoUpdateable(ItemInfo i) {
if (i instanceof ShortcutInfo) {
ShortcutInfo info = (ShortcutInfo) i;
// We need to check for ACTION_MAIN otherwise getComponent() might
// return null for some shortcuts (for instance, for shortcuts to
// web pages.)
Intent intent = info.intent;
ComponentName name = intent.getComponent();
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
Intent.ACTION_MAIN.equals(intent.getAction()) && name != null) {
return true;
}
// placeholder shortcuts get special treatment, let them through too.
if (info.isPromise()) {
return true;
}
}
return false;
/**
* @return true if the ShortcutInfo points to an app shortcut target, i.e. it has been added by
* dragging from AllApps list.
*/
public static boolean isShortcutAppTarget(ShortcutInfo info) {
// We need to check for ACTION_MAIN otherwise getComponent() might
// return null for some shortcuts (for instance, for shortcuts to
// web pages.)
Intent intent = info.promisedIntent != null ? info.promisedIntent : info.intent;
ComponentName name = intent.getComponent();
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION &&
Intent.ACTION_MAIN.equals(intent.getAction()) && name != null;
}
/**
+11 -1
View File
@@ -87,11 +87,21 @@ public class ShortcutInfo extends ItemInfo {
*/
private Bitmap mIcon;
/**
* Indicates that the icon is disabled due to safe mode restrictions.
*/
public static final int FLAG_DISABLED_SAFEMODE = 1;
/**
* Indicates that the icon is disabled as the app is not available.
*/
public static final int FLAG_DISABLED_NOT_AVAILABLE = 2;
/**
* Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
* sd-card is not available).
*/
boolean isDisabled = false;
int isDisabled = DEFAULT;
int status;
+35 -2
View File
@@ -4629,6 +4629,34 @@ public class Workspace extends SmoothPagedView
});
}
public void disableShortcutsByPackageName(final ArrayList<String> packages,
final UserHandleCompat user, final int reason) {
final HashSet<String> packageNames = new HashSet<String>();
packageNames.addAll(packages);
mapOverItems(MAP_RECURSE, new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v, View parent) {
if (info instanceof ShortcutInfo && v instanceof BubbleTextView) {
ShortcutInfo shortcutInfo = (ShortcutInfo) info;
ComponentName cn = shortcutInfo.getTargetComponent();
if (user.equals(shortcutInfo.user) && cn != null
&& packageNames.contains(cn.getPackageName())) {
shortcutInfo.isDisabled |= reason;
BubbleTextView shortcut = (BubbleTextView) v;
shortcut.applyFromShortcutInfo(shortcutInfo, mIconCache, true, false);
if (parent != null) {
parent.invalidate();
}
}
}
// process all the shortcuts
return false;
}
});
}
// Removes ALL items that match a given package name, this is usually called when a package
// has been removed and we want to remove all components (widgets, shortcuts, apps) that
// belong to that package.
@@ -4859,7 +4887,6 @@ public class Workspace extends SmoothPagedView
ComponentName cn = shortcutInfo.getTargetComponent();
AppInfo appInfo = appsMap.get(cn);
if (user.equals(shortcutInfo.user) && cn != null
&& LauncherModel.isShortcutInfoUpdateable(info)
&& pkgNames.contains(cn.getPackageName())) {
boolean promiseStateChanged = false;
boolean infoUpdated = false;
@@ -4904,8 +4931,14 @@ public class Workspace extends SmoothPagedView
LauncherModel.updateItemInDatabase(getContext(), shortcutInfo);
}
if ((shortcutInfo.isDisabled & ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE) != 0) {
// Since package was just updated, the target must be available now.
shortcutInfo.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
infoUpdated = true;
}
if (appInfo != null) {
// Only update the icon and labels if the shortcuts points to an app target
if ((appInfo != null) && LauncherModel.isShortcutAppTarget(shortcutInfo)) {
shortcutInfo.updateIcon(mIconCache);
shortcutInfo.title = appInfo.title.toString();
shortcutInfo.contentDescription = appInfo.contentDescription;
@@ -19,8 +19,10 @@ package com.android.launcher3.compat;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Rect;
import android.os.Build;
import android.os.Bundle;
import com.android.launcher3.Utilities;
@@ -73,4 +75,13 @@ public abstract class LauncherAppsCompat {
public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user);
public abstract boolean isActivityEnabledForProfile(ComponentName component,
UserHandleCompat user);
public boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
try {
ApplicationInfo info = pm.getApplicationInfo(packageName, flags);
return info != null && info.enabled;
} catch (NameNotFoundException e) {
return false;
}
}
}
@@ -22,7 +22,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
@@ -112,12 +111,7 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat {
}
public boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user) {
try {
PackageInfo info = mPm.getPackageInfo(packageName, 0);
return info != null && info.applicationInfo.enabled;
} catch (NameNotFoundException e) {
return false;
}
return isAppEnabled(mPm, packageName, 0);
}
public boolean isActivityEnabledForProfile(ComponentName component, UserHandleCompat user) {
@@ -198,8 +192,13 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat {
callback.onPackagesAvailable(packages, user, replacing);
}
} else if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING,
Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT);
// This intent is broadcasted when moving a package or mounting/un-mounting
// external storage.
// However on Kitkat this is also sent when a package is being updated, and
// contains an extra Intent.EXTRA_REPLACING=true for that case.
// Using false as default for Intent.EXTRA_REPLACING gives correct value on
// lower devices as the intent is not sent when the app is updating/replacing.
final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
String[] packages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
for (OnAppsChangedCallbackCompat callback : getCallbacks()) {
callback.onPackagesUnavailable(packages, user, replacing);