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:
parent
f076eae0ca
commit
44cba69638
|
@ -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.** {
|
||||
*;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2652,6 +2652,9 @@ public class Launcher extends Activity
|
|||
final ShortcutInfo shortcut = (ShortcutInfo) tag;
|
||||
|
||||
if (shortcut.isDisabled != 0) {
|
||||
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;
|
||||
|
@ -2659,6 +2662,7 @@ public class Launcher extends Activity
|
|||
Toast.makeText(this, error, Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for abandoned promise
|
||||
if ((v instanceof BubbleTextView)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@ import android.graphics.drawable.Drawable;
|
|||
|
||||
public abstract class LauncherActivityInfoCompat {
|
||||
|
||||
public static final int FLAG_SUSPENDED = 1<<30;
|
||||
|
||||
LauncherActivityInfoCompat() {
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -126,6 +126,10 @@ public class LauncherAppsCompatV16 extends LauncherAppsCompat {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isPackageSuspendedForProfile(String packageName, UserHandleCompat user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void unregisterForPackageIntents() {
|
||||
mContext.unregisterReceiver(mPackageMonitor);
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue