From a4b30072a090e266ff1aeef2e1641fecbaab0b2b Mon Sep 17 00:00:00 2001 From: Matt Pietal Date: Thu, 4 Apr 2019 13:44:36 -0400 Subject: [PATCH] Sharesheet - Add support for sunshine Set apps/targets to be visually disabled when in suspended state. Do not auto launch when the only app is suspended. Bug: 112553778 Test: Manually inspect colors on app suspension Change-Id: Ief9faba831c4f3676f02f84b0174b74d4103f940 --- .../android/internal/app/ChooserActivity.java | 28 +++++++--- .../internal/app/ResolverActivity.java | 52 ++++++++++++++++++- .../internal/app/ResolverListController.java | 11 +--- 3 files changed, 74 insertions(+), 17 deletions(-) diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index fcb32d54122cd..988fad2fb28e1 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -43,6 +43,7 @@ import android.content.IntentSender; import android.content.IntentSender.SendIntentException; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.LabeledIntent; import android.content.pm.LauncherApps; import android.content.pm.PackageManager; @@ -947,8 +948,11 @@ public class ChooserActivity extends ResolverActivity { // Note that this is only safe because the Intent handled by the ChooserActivity is // guaranteed to contain no extras unknown to the local ClassLoader. That is why this // method can not be replaced in the ResolverActivity whole hog. - return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, - super.shouldAutoLaunchSingleChoice(target)); + if (!super.shouldAutoLaunchSingleChoice(target)) { + return false; + } + + return getIntent().getBooleanExtra(Intent.EXTRA_AUTO_LAUNCH_SINGLE_CHOICE, true); } @Override @@ -973,10 +977,6 @@ public class ChooserActivity extends ResolverActivity { @Override protected boolean onTargetSelected(TargetInfo target, boolean alwaysCheck) { - if (target instanceof NotSelectableTargetInfo) { - return false; - } - if (mRefinementIntentSender != null) { final Intent fillIn = new Intent(); final List sourceIntents = target.getAllSourceIntents(); @@ -1009,6 +1009,11 @@ public class ChooserActivity extends ResolverActivity { @Override public void startSelected(int which, boolean always, boolean filtered) { + TargetInfo targetInfo = mChooserListAdapter.targetInfoForPosition(which, filtered); + if (targetInfo != null && targetInfo instanceof NotSelectableTargetInfo) { + return; + } + final long selectionCost = System.currentTimeMillis() - mChooserShownTime; super.startSelected(which, always, filtered); @@ -1564,6 +1569,10 @@ public class ChooserActivity extends ResolverActivity { public ChooserTarget getChooserTarget() { return null; } + + public boolean isSuspended() { + return false; + } } final class PlaceHolderTargetInfo extends NotSelectableTargetInfo { @@ -1589,6 +1598,7 @@ public class ChooserActivity extends ResolverActivity { private final Intent mFillInIntent; private final int mFillInFlags; private final float mModifiedScore; + private boolean mIsSuspended; SelectableTargetInfo(DisplayResolveInfo sourceInfo, ChooserTarget chooserTarget, float modifiedScore) { @@ -1617,6 +1627,8 @@ public class ChooserActivity extends ResolverActivity { mFillInIntent = null; mFillInFlags = 0; + ApplicationInfo ai = sourceInfo.getResolveInfo().activityInfo.applicationInfo; + mIsSuspended = (ai.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; } private SelectableTargetInfo(SelectableTargetInfo other, Intent fillInIntent, int flags) { @@ -1631,6 +1643,10 @@ public class ChooserActivity extends ResolverActivity { mModifiedScore = other.mModifiedScore; } + public boolean isSuspended() { + return mIsSuspended; + } + /** * Since ShortcutInfos are returned by ShortcutManager, we can cache the shortcuts and skip * the call to LauncherApps#getShortcuts(ShortcutQuery). diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 47b965d2e2459..ad1e767f011a6 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -44,6 +44,8 @@ import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Bitmap; +import android.graphics.ColorMatrix; +import android.graphics.ColorMatrixColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -132,6 +134,8 @@ public class ResolverActivity extends Activity { private boolean mRegistered; + private ColorMatrixColorFilter mSuspendedMatrixColorFilter; + /** See {@link #setRetainInOnStop}. */ private boolean mRetainInOnStop; @@ -350,6 +354,8 @@ public class ResolverActivity extends Activity { bindProfileView(); } + initSuspendedColorMatrix(); + if (isVoiceInteraction()) { onSetupVoiceInteraction(); } @@ -367,6 +373,25 @@ public class ResolverActivity extends Activity { mAdapter.handlePackagesChanged(); } + private void initSuspendedColorMatrix() { + int grayValue = 127; + float scale = 0.5f; // half bright + + ColorMatrix tempBrightnessMatrix = new ColorMatrix(); + float[] mat = tempBrightnessMatrix.getArray(); + mat[0] = scale; + mat[6] = scale; + mat[12] = scale; + mat[4] = grayValue; + mat[9] = grayValue; + mat[14] = grayValue; + + ColorMatrix matrix = new ColorMatrix(); + matrix.setSaturation(0.0f); + matrix.preConcat(tempBrightnessMatrix); + mSuspendedMatrixColorFilter = new ColorMatrixColorFilter(matrix); + } + /** * Perform any initialization needed for voice interaction. */ @@ -1019,7 +1044,14 @@ public class ResolverActivity extends Activity { if (target != null) { safelyStartActivity(target); + + // Rely on the ActivityManager to pop up a dialog regarding app suspension + // and return false + if (target.isSuspended()) { + return false; + } } + return true; } @@ -1106,7 +1138,7 @@ public class ResolverActivity extends Activity { } public boolean shouldAutoLaunchSingleChoice(TargetInfo target) { - return true; + return !target.isSuspended(); } public void showTargetDetails(ResolveInfo ri) { @@ -1326,6 +1358,7 @@ public class ResolverActivity extends Activity { private final CharSequence mExtendedInfo; private final Intent mResolvedIntent; private final List mSourceIntents = new ArrayList<>(); + private boolean mIsSuspended; public DisplayResolveInfo(Intent originalIntent, ResolveInfo pri, CharSequence pLabel, CharSequence pInfo, Intent pOrigIntent) { @@ -1341,6 +1374,8 @@ public class ResolverActivity extends Activity { final ActivityInfo ai = mResolveInfo.activityInfo; intent.setComponent(new ComponentName(ai.applicationInfo.packageName, ai.name)); + mIsSuspended = (ai.applicationInfo.flags & ApplicationInfo.FLAG_SUSPENDED) != 0; + mResolvedIntent = intent; } @@ -1410,7 +1445,6 @@ public class ResolverActivity extends Activity { @Override public boolean startAsCaller(ResolverActivity activity, Bundle options, int userId) { - if (mEnableChooserDelegate) { return activity.startAsCallerImpl(mResolvedIntent, options, false, userId); } else { @@ -1425,6 +1459,9 @@ public class ResolverActivity extends Activity { return false; } + public boolean isSuspended() { + return mIsSuspended; + } } List getDisplayList() { @@ -1520,6 +1557,11 @@ public class ResolverActivity extends Activity { * @return the list of supported source intents deduped against this single target */ List getAllSourceIntents(); + + /** + * @return true if this target can be selected by the user + */ + boolean isSuspended(); } public class ResolveListAdapter extends BaseAdapter { @@ -1957,6 +1999,12 @@ public class ResolverActivity extends Activity { holder.text2.setText(subLabel); } + if (info.isSuspended()) { + holder.icon.setColorFilter(mSuspendedMatrixColorFilter); + } else { + holder.icon.setColorFilter(null); + } + if (info instanceof DisplayResolveInfo && !((DisplayResolveInfo) info).hasDisplayIcon()) { new LoadIconTask((DisplayResolveInfo) info, holder.icon).execute(); diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java index d12c51b56b46e..a3cfa8786d594 100644 --- a/core/java/com/android/internal/app/ResolverListController.java +++ b/core/java/com/android/internal/app/ResolverListController.java @@ -25,7 +25,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.os.RemoteException; @@ -153,11 +152,6 @@ public class ResolverListController { } // Filter out any activities that the launched uid does not have permission for. - // - // Also filter out those that are suspended because they couldn't be started. We don't do this - // when we have an explicit list of resolved activities, because that only happens when - // we are being subclassed, so we can safely launch whatever they gave us. - // // To preserve the inputList, optionally will return the original list if any modification has // been made. @VisibleForTesting @@ -171,9 +165,8 @@ public class ResolverListController { int granted = ActivityManager.checkComponentPermission( ai.permission, mLaunchedFromUid, ai.applicationInfo.uid, ai.exported); - boolean suspended = (ai.applicationInfo.flags - & ApplicationInfo.FLAG_SUSPENDED) != 0; - if (granted != PackageManager.PERMISSION_GRANTED || suspended + + if (granted != PackageManager.PERMISSION_GRANTED || isComponentFiltered(ai.getComponentName())) { // Access not allowed! We're about to filter an item, // so modify the unfiltered version if it hasn't already been modified.