Grey out suspended applications.

Grey out application shortcuts and all apps entries
for packages that are suspended.

Bug: 22776761
Change-Id: I1b63da1816aca1de52b9f9bee62d1b162d0cdf4d
This commit is contained in:
Kenny Guy 2016-01-21 19:50:02 +00:00 committed by Rubin Xu
parent f076eae0ca
commit 44cba69638
13 changed files with 199 additions and 17 deletions

View File

@ -70,3 +70,10 @@
public float getBackgroundAlpha();
public void setBackgroundAlpha(float);
}
# Proguard will strip new callbacks in LauncherApps.Callback from
# WrappedCallback if compiled against an older SDK. Don't let this happen.
-keep class com.android.launcher3.compat.** {
*;
}

View File

@ -118,6 +118,25 @@ class AllAppsList {
}
}
/**
* Suspend the apps for the given apk identified by packageName.
*/
public void suspendPackage(String packageName, UserHandleCompat user, boolean suspend) {
final List<AppInfo> data = this.data;
for (int i = data.size() - 1; i >= 0; i--) {
AppInfo info = data.get(i);
final ComponentName component = info.intent.getComponent();
if (info.user.equals(user) && packageName.equals(component.getPackageName())) {
if (suspend) {
info.isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
} else {
info.isDisabled &= ~ShortcutInfo.FLAG_DISABLED_SUSPENDED;
}
modified.add(info);
}
}
}
public void updateIconsAndLabels(HashSet<String> packages, UserHandleCompat user,
ArrayList<AppInfo> outUpdates) {
for (AppInfo info : data) {

View File

@ -56,6 +56,11 @@ public class AppInfo extends ItemInfo {
int flags = 0;
/**
* {@see ShortcutInfo#isDisabled}
*/
int isDisabled = ShortcutInfo.DEFAULT;
AppInfo() {
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
}
@ -75,8 +80,10 @@ public class AppInfo extends ItemInfo {
IconCache iconCache) {
this.componentName = info.getComponentName();
this.container = ItemInfo.NO_ID;
flags = initFlags(info);
if ((info.getApplicationInfo().flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0) {
isDisabled |= ShortcutInfo.FLAG_DISABLED_SUSPENDED;
}
iconCache.getTitleAndIcon(this, info, true /* useLowResIcon */);
intent = makeLaunchIntent(context, info, user);
this.user = user;
@ -101,6 +108,7 @@ public class AppInfo extends ItemInfo {
title = Utilities.trim(info.title);
intent = new Intent(info.intent);
flags = info.flags;
isDisabled = info.isDisabled;
iconBitmap = info.iconBitmap;
}
@ -140,4 +148,9 @@ public class AppInfo extends ItemInfo {
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED)
.putExtra(EXTRA_PROFILE, serialNumber);
}
@Override
public boolean isDisabled() {
return isDisabled != 0;
}
}

View File

@ -150,7 +150,7 @@ public class BubbleTextView extends TextView
Bitmap b = info.getIcon(iconCache);
FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(b);
if (info.isDisabled != 0) {
if (info.isDisabled()) {
iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
}
setIcon(iconDrawable, mIconSize);
@ -166,7 +166,11 @@ public class BubbleTextView extends TextView
}
public void applyFromApplicationInfo(AppInfo info) {
setIcon(mLauncher.createIconDrawable(info.iconBitmap), mIconSize);
FastBitmapDrawable iconDrawable = mLauncher.createIconDrawable(info.iconBitmap);
if (info.isDisabled()) {
iconDrawable.setState(FastBitmapDrawable.State.DISABLED);
}
setIcon(iconDrawable, mIconSize);
setText(info.title);
if (info.contentDescription != null) {
setContentDescription(info.contentDescription);
@ -250,11 +254,11 @@ public class BubbleTextView extends TextView
private void updateIconState() {
if (mIcon instanceof FastBitmapDrawable) {
FastBitmapDrawable d = (FastBitmapDrawable) mIcon;
if (isPressed() || mStayPressed) {
d.animateState(FastBitmapDrawable.State.PRESSED);
} else if (getTag() instanceof ShortcutInfo
&& ((ShortcutInfo) getTag()).isDisabled != 0) {
if (getTag() instanceof ItemInfo
&& ((ItemInfo) getTag()).isDisabled()) {
d.animateState(FastBitmapDrawable.State.DISABLED);
} else if (isPressed() || mStayPressed) {
d.animateState(FastBitmapDrawable.State.PRESSED);
} else {
d.animateState(FastBitmapDrawable.State.NORMAL);
}

View File

@ -198,4 +198,11 @@ public class ItemInfo {
+ " screen=" + screenId + " cellX=" + cellX + " cellY=" + cellY + " spanX=" + spanX
+ " spanY=" + spanY + " user=" + user + ")";
}
/**
* Whether this item is disabled.
*/
public boolean isDisabled() {
return false;
}
}

View File

@ -2652,12 +2652,16 @@ public class Launcher extends Activity
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;
if ((shortcut.isDisabled & ShortcutInfo.FLAG_DISABLED_SUSPENDED) != 0) {
// Launch activity anyway, framework will tell the user why the app is suspended.
} else {
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;
}
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
return;
}
// Check for abandoned promise

View File

@ -1198,6 +1198,20 @@ public class LauncherModel extends BroadcastReceiver
}
}
@Override
public void onPackagesSuspended(String[] packageNames, UserHandleCompat user) {
enqueuePackageUpdated(new PackageUpdatedTask(
PackageUpdatedTask.OP_SUSPEND, packageNames,
user));
}
@Override
public void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user) {
enqueuePackageUpdated(new PackageUpdatedTask(
PackageUpdatedTask.OP_UNSUSPEND, packageNames,
user));
}
/**
* Call from the handler for ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and
* ACTION_PACKAGE_CHANGED.
@ -1777,6 +1791,11 @@ public class LauncherModel extends BroadcastReceiver
restoredRows.add(id);
restored = false;
}
boolean isSuspended = launcherApps.isPackageSuspendedForProfile(
cn.getPackageName(), user);
if (isSuspended) {
disabledState = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
}
} else if (validPkg) {
intent = null;
if ((promiseType & ShortcutInfo.FLAG_AUTOINTALL_ICON) != 0) {
@ -2892,6 +2911,8 @@ public class LauncherModel extends BroadcastReceiver
public static final int OP_UPDATE = 2;
public static final int OP_REMOVE = 3; // uninstlled
public static final int OP_UNAVAILABLE = 4; // external media unmounted
public static final int OP_SUSPEND = 5; // package suspended
public static final int OP_UNSUSPEND = 6; // package unsuspended
public PackageUpdatedTask(int op, String[] packages, UserHandleCompat user) {
@ -2949,6 +2970,15 @@ public class LauncherModel extends BroadcastReceiver
mApp.getWidgetCache().removePackage(packages[i], mUser);
}
break;
case OP_SUSPEND:
case OP_UNSUSPEND:
boolean suspend = mOp == OP_SUSPEND;
for (int i=0; i<N; i++) {
if (DEBUG_LOADERS) Log.d(TAG, "mAllAppsList.suspendPackage "
+ suspend + " " + packages[i]);
mBgAllAppsList.suspendPackage(packages[i], mUser, suspend);
}
break;
}
ArrayList<AppInfo> added = null;
@ -3001,7 +3031,7 @@ public class LauncherModel extends BroadcastReceiver
}
// Update shortcut infos
if (mOp == OP_ADD || mOp == OP_UPDATE) {
if (mOp == OP_ADD || mOp == OP_UPDATE || mOp == OP_UNSUSPEND) {
final ArrayList<ShortcutInfo> updatedShortcuts = new ArrayList<ShortcutInfo>();
final ArrayList<ShortcutInfo> removedShortcuts = new ArrayList<ShortcutInfo>();
final ArrayList<LauncherAppWidgetInfo> widgets = new ArrayList<LauncherAppWidgetInfo>();
@ -3014,6 +3044,11 @@ public class LauncherModel extends BroadcastReceiver
boolean infoUpdated = false;
boolean shortcutUpdated = false;
if (mOp == OP_UNSUSPEND) {
si.isDisabled &= ~ ShortcutInfo.FLAG_DISABLED_SUSPENDED;
infoUpdated = true;
}
// Update shortcuts which use iconResource.
if ((si.iconResource != null)
&& packageSet.contains(si.iconResource.packageName)) {
@ -3139,7 +3174,7 @@ public class LauncherModel extends BroadcastReceiver
final ArrayList<String> removedPackageNames =
new ArrayList<String>();
if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE) {
if (mOp == OP_REMOVE || mOp == OP_UNAVAILABLE || mOp == OP_SUSPEND) {
// Mark all packages in the broadcast to be removed
removedPackageNames.addAll(Arrays.asList(packages));
} else if (mOp == OP_UPDATE) {
@ -3155,6 +3190,8 @@ public class LauncherModel extends BroadcastReceiver
final int removeReason;
if (mOp == OP_UNAVAILABLE) {
removeReason = ShortcutInfo.FLAG_DISABLED_NOT_AVAILABLE;
} else if (mOp == OP_SUSPEND) {
removeReason = ShortcutInfo.FLAG_DISABLED_SUSPENDED;
} else {
// Remove all the components associated with this package
for (String pn : removedPackageNames) {

View File

@ -111,6 +111,11 @@ public class ShortcutInfo extends ItemInfo {
*/
public static final int FLAG_DISABLED_NOT_AVAILABLE = 2;
/**
* Indicates that the icon is disabled as the app is suspended
*/
public static final int FLAG_DISABLED_SUSPENDED = 4;
/**
* Could be disabled, if the the app is installed but unavailable (eg. in safe mode or when
* sd-card is not available).
@ -177,6 +182,7 @@ public class ShortcutInfo extends ItemInfo {
intent = new Intent(info.intent);
customIcon = false;
flags = info.flags;
isDisabled = info.isDisabled;
}
public void setIcon(Bitmap b) {
@ -278,5 +284,10 @@ public class ShortcutInfo extends ItemInfo {
shortcut.flags = AppInfo.initFlags(info);
return shortcut;
}
@Override
public boolean isDisabled() {
return isDisabled != 0;
}
}

View File

@ -24,6 +24,8 @@ import android.graphics.drawable.Drawable;
public abstract class LauncherActivityInfoCompat {
public static final int FLAG_SUSPENDED = 1<<30;
LauncherActivityInfoCompat() {
}

View File

@ -42,6 +42,8 @@ public abstract class LauncherAppsCompat {
void onPackageChanged(String packageName, UserHandleCompat user);
void onPackagesAvailable(String[] packageNames, UserHandleCompat user, boolean replacing);
void onPackagesUnavailable(String[] packageNames, UserHandleCompat user, boolean replacing);
void onPackagesSuspended(String[] packageNames, UserHandleCompat user);
void onPackagesUnsuspended(String[] packageNames, UserHandleCompat user);
}
protected LauncherAppsCompat() {
@ -53,7 +55,9 @@ public abstract class LauncherAppsCompat {
public static LauncherAppsCompat getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
if (Utilities.ATLEAST_LOLLIPOP) {
if (Utilities.isNycOrAbove()) {
sInstance = new LauncherAppsCompatVN(context.getApplicationContext());
} else if (Utilities.ATLEAST_LOLLIPOP) {
sInstance = new LauncherAppsCompatVL(context.getApplicationContext());
} else {
sInstance = new LauncherAppsCompatV16(context.getApplicationContext());
@ -75,6 +79,7 @@ public abstract class LauncherAppsCompat {
public abstract boolean isPackageEnabledForProfile(String packageName, UserHandleCompat user);
public abstract boolean isActivityEnabledForProfile(ComponentName component,
UserHandleCompat user);
public abstract boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user);
public boolean isAppEnabled(PackageManager pm, String packageName, int flags) {
try {
@ -84,4 +89,4 @@ public abstract class LauncherAppsCompat {
return false;
}
}
}
}

View File

@ -126,6 +126,10 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat {
}
}
public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
return false;
}
private void unregisterForPackageIntents() {
mContext.unregisterReceiver(mPackageMonitor);
}

View File

@ -36,7 +36,7 @@ import java.util.Map;
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class LauncherAppsCompatVL extends LauncherAppsCompat {
private LauncherApps mLauncherApps;
protected LauncherApps mLauncherApps;
private Map<OnAppsChangedCallbackCompat, WrappedCallback> mCallbacks
= new HashMap<OnAppsChangedCallbackCompat, WrappedCallback>();
@ -106,6 +106,10 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
return mLauncherApps.isActivityEnabled(component, user.getUser());
}
public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
return false;
}
private static class WrappedCallback extends LauncherApps.Callback {
private LauncherAppsCompat.OnAppsChangedCallbackCompat mCallback;
@ -134,6 +138,14 @@ public class LauncherAppsCompatVL extends LauncherAppsCompat {
mCallback.onPackagesUnavailable(packageNames, UserHandleCompat.fromUser(user),
replacing);
}
public void onPackagesSuspended(String[] packageNames, UserHandle user) {
mCallback.onPackagesSuspended(packageNames, UserHandleCompat.fromUser(user));
}
public void onPackagesUnsuspended(String[] packageNames, UserHandle user) {
mCallback.onPackagesUnsuspended(packageNames, UserHandleCompat.fromUser(user));
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (C) 2016 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.LauncherApps;
import android.os.UserHandle;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//TODO: Once gogole3 SDK is updated to N, add @TargetApi(Build.VERSION_CODES.N)
public class LauncherAppsCompatVN extends LauncherAppsCompatVL {
private static final String TAG = "LauncherAppsCompatVN";
LauncherAppsCompatVN(Context context) {
super(context);
}
@Override
public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
if (user != null && packageName != null) {
try {
//TODO: Replace with proper API call once google3 SDK is updated.
Method getApplicationInfoMethod = LauncherApps.class.getMethod("getApplicationInfo",
String.class, int.class, UserHandle.class);
ApplicationInfo info = (ApplicationInfo) getApplicationInfoMethod.invoke(
mLauncherApps, packageName, 0, user.getUser());
if (info != null) {
return (info.flags & LauncherActivityInfoCompat.FLAG_SUSPENDED) != 0;
}
} catch (NoSuchMethodError | NoSuchMethodException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException e) {
Log.e(TAG, "Running on N without getApplicationInfo", e);
}
}
return false;
}
}