Raster and badge directshare with IconFactory
Removes badge view and id. Unifies permission check for icon
loading. Fixes a threading issue in IconFactory so badging works
properly. Partly setting up for label and sublabel loading in a
future commit.
Fixes: 126568207
Test: manual
Change-Id: I265d08cc3a5c1499da252307fea6e28b22d59066
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index f250666..ab8177f 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -58,6 +58,7 @@
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.metrics.LogMaker;
@@ -1463,14 +1464,6 @@
return null;
}
- public Drawable getBadgeIcon() {
- return null;
- }
-
- public CharSequence getBadgeContentDescription() {
- return null;
- }
-
public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
return null;
}
@@ -1559,31 +1552,49 @@
*/
// TODO(121287224): Refactor code to apply the suggestion above
private Drawable getChooserTargetIconDrawable(ChooserTarget target) {
+ Drawable directShareIcon = null;
+
+ // First get the target drawable and associated activity info
final Icon icon = target.getIcon();
if (icon != null) {
- return icon.loadDrawable(ChooserActivity.this);
- }
- if (!USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) {
- return null;
+ directShareIcon = icon.loadDrawable(ChooserActivity.this);
+ } else if (USE_SHORTCUT_MANAGER_FOR_DIRECT_TARGETS) {
+ Bundle extras = target.getIntentExtras();
+ if (extras != null && extras.containsKey(Intent.EXTRA_SHORTCUT_ID)) {
+ CharSequence shortcutId = extras.getCharSequence(Intent.EXTRA_SHORTCUT_ID);
+ LauncherApps launcherApps = (LauncherApps) getSystemService(
+ Context.LAUNCHER_APPS_SERVICE);
+ final LauncherApps.ShortcutQuery q = new LauncherApps.ShortcutQuery();
+ q.setPackage(target.getComponentName().getPackageName());
+ q.setShortcutIds(Arrays.asList(shortcutId.toString()));
+ q.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC);
+ final List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(q, getUser());
+ if (shortcuts != null && shortcuts.size() > 0) {
+ directShareIcon = launcherApps.getShortcutIconDrawable(shortcuts.get(0), 0);
+ }
+ }
}
- Bundle extras = target.getIntentExtras();
- if (extras == null || !extras.containsKey(Intent.EXTRA_SHORTCUT_ID)) {
- return null;
- }
- CharSequence shortcutId = extras.getCharSequence(Intent.EXTRA_SHORTCUT_ID);
- LauncherApps launcherApps = (LauncherApps) getSystemService(
- Context.LAUNCHER_APPS_SERVICE);
- final LauncherApps.ShortcutQuery q = new LauncherApps.ShortcutQuery();
- q.setPackage(target.getComponentName().getPackageName());
- q.setShortcutIds(Arrays.asList(shortcutId.toString()));
- q.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC);
- final List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(q, getUser());
- if (shortcuts != null && shortcuts.size() > 0) {
- return launcherApps.getShortcutIconDrawable(shortcuts.get(0), 0);
+ if (directShareIcon == null) return null;
+
+ ActivityInfo info = null;
+ try {
+ info = mPm.getActivityInfo(target.getComponentName(), 0);
+ } catch (NameNotFoundException error) {
+ Log.e(TAG, "Could not find activity associated with ChooserTarget");
}
- return null;
+ if (info == null) return null;
+
+ // Now fetch app icon and raster with no badging even in work profile
+ Bitmap appIcon = (new ActivityInfoPresentationGetter(info)).getIconBitmap();
+
+ // Raster target drawable with appIcon as a badge
+ SimpleIconFactory sif = SimpleIconFactory.obtain(ChooserActivity.this);
+ Bitmap directShareBadgedIcon = sif.createAppBadgedIconBitmap(directShareIcon, appIcon);
+ sif.recycle();
+
+ return new BitmapDrawable(getResources(), directShareBadgedIcon);
}
public float getModifiedScore() {
@@ -1681,16 +1692,6 @@
return mDisplayIcon;
}
- @Override
- public Drawable getBadgeIcon() {
- return mBadgeIcon;
- }
-
- @Override
- public CharSequence getBadgeContentDescription() {
- return mBadgeContentDescription;
- }
-
public ChooserTarget getChooserTarget() {
return mChooserTarget;
}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 12942ab..21152ae 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -42,7 +42,7 @@
import android.content.pm.UserInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Color;
+import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
@@ -133,8 +133,6 @@
/** See {@link #setRetainInOnStop}. */
private boolean mRetainInOnStop;
- SimpleIconFactory mSimpleIconFactory;
-
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override public void onSomePackagesChanged() {
mAdapter.handlePackagesChanged();
@@ -311,11 +309,6 @@
// as to mitigate Intent Capturing vulnerability
mSupportsAlwaysUseOption = supportsAlwaysUseOption && !mUseLayoutForBrowsables;
- final int iconSize = getResources().getDimensionPixelSize(R.dimen.resolver_icon_size);
- final int badgeSize = getResources().getDimensionPixelSize(R.dimen.resolver_badge_size);
- mSimpleIconFactory = new SimpleIconFactory(this, mIconDpi, iconSize, badgeSize);
- mSimpleIconFactory.setWrapperBackgroundColor(Color.WHITE);
-
if (configureContentView(mIntents, initialIntents, rList)) {
return;
}
@@ -500,64 +493,150 @@
}
}
- @Nullable
- Drawable getIcon(Resources res, int resId) {
- Drawable result;
- try {
- result = res.getDrawableForDensity(resId, mIconDpi);
- } catch (Resources.NotFoundException e) {
- result = null;
- }
-
- return result;
- }
/**
- * Loads the icon for the provided ResolveInfo. Defaults to using the application icon over
+ * Loads the icon for the provided ApplicationInfo. Defaults to using the application icon over
* any IntentFilter or Activity icon to increase user understanding, with an exception for
* applications that hold the right permission. Always attempts to use icon resources over
* PackageManager loading mechanisms so badging can be done by iconloader.
*/
- Drawable loadIconForResolveInfo(ResolveInfo ri) {
- Drawable dr = null;
+ private abstract class TargetPresentationGetter {
+ @Nullable abstract Drawable getIconSubstitute();
+ @Nullable abstract String getAppSubLabel();
- // Allow for app icon override given the right permission
- if (PackageManager.PERMISSION_GRANTED == mPm.checkPermission(
- android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON,
- ri.activityInfo.applicationInfo.packageName)) {
- try {
- if (ri.resolvePackageName != null && ri.icon != 0) {
- dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
- }
- if (dr == null) {
- final int iconRes = ri.getIconResource();
- if (iconRes != 0) {
- dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName),
- iconRes);
+ private final ApplicationInfo mAi;
+ private final boolean mHasSubstitutePermission;
+
+ TargetPresentationGetter(ApplicationInfo ai) {
+ mAi = ai;
+ mHasSubstitutePermission = PackageManager.PERMISSION_GRANTED == mPm.checkPermission(
+ android.Manifest.permission.SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON,
+ mAi.packageName);
+ }
+
+ Drawable getIcon() {
+ return new BitmapDrawable(getResources(), getIconBitmap());
+ }
+
+ Bitmap getIconBitmap() {
+ Drawable dr = null;
+ if (mHasSubstitutePermission) {
+ dr = getIconSubstitute();
+ }
+
+ if (dr == null) {
+ try {
+ if (mAi.icon != 0) {
+ dr = loadIconFromResource(mPm.getResourcesForApplication(mAi), mAi.icon);
}
+ } catch (NameNotFoundException ignore) {
+ }
+ }
+
+ // Fall back to ApplicationInfo#loadIcon if nothing has been loaded
+ if (dr == null) {
+ dr = mAi.loadIcon(mPm);
+ }
+
+ SimpleIconFactory sif = SimpleIconFactory.obtain(ResolverActivity.this);
+ Bitmap icon = sif.createUserBadgedIconBitmap(dr, Process.myUserHandle());
+ sif.recycle();
+
+ return icon;
+ }
+
+ String getLabel() {
+ String label = null;
+ // Apps with the substitute permission will always show the sublabel as their label
+ if (mHasSubstitutePermission) {
+ label = getAppSubLabel();
+ }
+
+ if (label == null) {
+ label = (String) mAi.loadLabel(mPm);
+ }
+
+ return label;
+ }
+
+ String getSubLabel() {
+ // Apps with the substitute permission will never have a sublabel
+ if (mHasSubstitutePermission) return null;
+ return getAppSubLabel();
+ }
+
+ @Nullable
+ protected Drawable loadIconFromResource(Resources res, int resId) {
+ return res.getDrawableForDensity(resId, mIconDpi);
+ }
+
+ }
+
+ protected class ResolveInfoPresentationGetter extends TargetPresentationGetter {
+
+ private final ResolveInfo mRi;
+
+ ResolveInfoPresentationGetter(ResolveInfo ri) {
+ super(ri.activityInfo.applicationInfo);
+ mRi = ri;
+ }
+
+ @Override
+ Drawable getIconSubstitute() {
+ Drawable dr = null;
+ try {
+ // Do not use ResolveInfo#getIconResource() as it defaults to the app
+ if (mRi.resolvePackageName != null && mRi.icon != 0) {
+ dr = loadIconFromResource(
+ mPm.getResourcesForApplication(mRi.resolvePackageName), mRi.icon);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON permission granted but "
+ "couldn't find resources for package", e);
}
+
+ return dr;
}
- // Use app icons for better user association
- if (dr == null) {
+ @Override
+ String getAppSubLabel() {
+ return (String) mRi.loadLabel(mPm);
+ }
+ }
+
+ protected class ActivityInfoPresentationGetter extends TargetPresentationGetter {
+ private final ActivityInfo mActivityInfo;
+ protected ActivityInfoPresentationGetter(ActivityInfo activityInfo) {
+ super(activityInfo.applicationInfo);
+ mActivityInfo = activityInfo;
+ }
+
+ @Override
+ Drawable getIconSubstitute() {
+ Drawable dr = null;
try {
- dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.applicationInfo),
- ri.activityInfo.applicationInfo.icon);
- } catch (NameNotFoundException ignore) {
+ // Do not use ActivityInfo#getIconResource() as it defaults to the app
+ if (mActivityInfo.icon != 0) {
+ dr = loadIconFromResource(
+ mPm.getResourcesForApplication(mActivityInfo.applicationInfo),
+ mActivityInfo.icon);
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON permission granted but "
+ + "couldn't find resources for package", e);
}
+
+ return dr;
}
- // Fall back to ApplicationInfo#loadIcon if nothing has been loaded
- if (dr == null) {
- dr = ri.activityInfo.applicationInfo.loadIcon(mPm);
+ @Override
+ String getAppSubLabel() {
+ return (String) mActivityInfo.loadLabel(mPm);
}
+ }
- return new BitmapDrawable(this.getResources(),
- mSimpleIconFactory.createUserBadgedIconBitmap(dr, Process.myUserHandle()));
+ Drawable loadIconForResolveInfo(ResolveInfo ri) {
+ return (new ResolveInfoPresentationGetter(ri)).getIcon();
}
@Override
@@ -1250,33 +1329,6 @@
return mDisplayIcon;
}
- public Drawable getBadgeIcon() {
- // We only expose a badge if we have extended info.
- // The badge is a higher-priority disambiguation signal
- // but we don't need one if we wouldn't show extended info at all.
- if (TextUtils.isEmpty(getExtendedInfo())) {
- return null;
- }
-
- if (mBadge == null && mResolveInfo != null && mResolveInfo.activityInfo != null
- && mResolveInfo.activityInfo.applicationInfo != null) {
- if (mResolveInfo.activityInfo.icon == 0 || mResolveInfo.activityInfo.icon
- == mResolveInfo.activityInfo.applicationInfo.icon) {
- // Badging an icon with exactly the same icon is silly.
- // If the activityInfo icon resid is 0 it will fall back
- // to the application's icon, making it a match.
- return null;
- }
- mBadge = mResolveInfo.activityInfo.applicationInfo.loadIcon(mPm);
- }
- return mBadge;
- }
-
- @Override
- public CharSequence getBadgeContentDescription() {
- return null;
- }
-
@Override
public TargetInfo cloneFilledIn(Intent fillInIntent, int flags) {
return new DisplayResolveInfo(this, fillInIntent, flags);
@@ -1413,21 +1465,11 @@
CharSequence getExtendedInfo();
/**
- * @return The drawable that should be used to represent this target
+ * @return The drawable that should be used to represent this target including badge
*/
Drawable getDisplayIcon();
/**
- * @return The (small) icon to badge the target with
- */
- Drawable getBadgeIcon();
-
- /**
- * @return The content description for the badge icon
- */
- CharSequence getBadgeContentDescription();
-
- /**
* Clone this target with the given fill-in information.
*/
TargetInfo cloneFilledIn(Intent fillInIntent, int flags);
@@ -1963,16 +2005,6 @@
new LoadAdapterIconTask((DisplayResolveInfo) info).execute();
}
holder.icon.setImageDrawable(info.getDisplayIcon());
- if (holder.badge != null) {
- final Drawable badge = info.getBadgeIcon();
- if (badge != null) {
- holder.badge.setImageDrawable(badge);
- holder.badge.setContentDescription(info.getBadgeContentDescription());
- holder.badge.setVisibility(View.VISIBLE);
- } else {
- holder.badge.setVisibility(View.GONE);
- }
- }
}
}
@@ -2027,13 +2059,11 @@
public TextView text;
public TextView text2;
public ImageView icon;
- public ImageView badge;
public ViewHolder(View view) {
text = (TextView) view.findViewById(com.android.internal.R.id.text1);
text2 = (TextView) view.findViewById(com.android.internal.R.id.text2);
icon = (ImageView) view.findViewById(R.id.icon);
- badge = (ImageView) view.findViewById(R.id.target_badge);
}
}
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
index eb1530e..a85485d 100644
--- a/core/java/com/android/internal/app/SimpleIconFactory.java
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -16,11 +16,13 @@
package com.android.internal.app;
+import static android.content.Context.ACTIVITY_SERVICE;
import static android.graphics.Paint.DITHER_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -42,6 +44,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.util.AttributeSet;
+import android.util.Pools.SynchronizedPool;
import com.android.internal.R;
@@ -58,6 +61,9 @@
@Deprecated
public class SimpleIconFactory {
+ private static final SynchronizedPool<SimpleIconFactory> sPool =
+ new SynchronizedPool<>(Runtime.getRuntime().availableProcessors());
+
private static final int DEFAULT_WRAPPER_BACKGROUND = Color.WHITE;
private static final float BLUR_FACTOR = 0.5f / 48;
@@ -74,10 +80,45 @@
private final Rect mOldBounds = new Rect();
/**
+ * Obtain a SimpleIconFactory from a pool objects.
+ *
* @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
*/
@Deprecated
- SimpleIconFactory(Context context, int fillResIconDpi, int iconBitmapSize,
+ public static SimpleIconFactory obtain(Context ctx) {
+ SimpleIconFactory instance = sPool.acquire();
+ if (instance == null) {
+ final ActivityManager am = (ActivityManager) ctx.getSystemService(ACTIVITY_SERVICE);
+ final int iconDpi = (am == null) ? 0 : am.getLauncherLargeIconDensity();
+
+ final Resources r = ctx.getResources();
+ final int iconSize = r.getDimensionPixelSize(R.dimen.resolver_icon_size);
+ final int badgeSize = r.getDimensionPixelSize(R.dimen.resolver_badge_size);
+
+ instance = new SimpleIconFactory(ctx, iconDpi, iconSize, badgeSize);
+ instance.setWrapperBackgroundColor(Color.WHITE);
+ }
+
+ return instance;
+ }
+
+ /**
+ * Recycles the SimpleIconFactory so others may use it.
+ *
+ * @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
+ */
+ @Deprecated
+ public void recycle() {
+ // Return to default background color
+ setWrapperBackgroundColor(Color.WHITE);
+ sPool.release(this);
+ }
+
+ /**
+ * @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
+ */
+ @Deprecated
+ private SimpleIconFactory(Context context, int fillResIconDpi, int iconBitmapSize,
int badgeBitmapSize) {
mContext = context.getApplicationContext();
mPm = mContext.getPackageManager();
@@ -170,7 +211,7 @@
* @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
*/
@Deprecated
- public Bitmap createAppBadgedIconBitmap(@Nullable Drawable icon, Bitmap renderedAppIcon) {
+ Bitmap createAppBadgedIconBitmap(@Nullable Drawable icon, Bitmap renderedAppIcon) {
// Flatten the passed in icon
float [] scale = new float[1];
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 71c153f..a24f892 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -27,22 +27,13 @@
android:focusable="true"
android:background="?attr/selectableItemBackgroundBorderless">
- <FrameLayout android:layout_width="wrap_content"
- android:layout_height="wrap_content">
- <ImageView android:id="@+id/icon"
- android:layout_width="48dp"
- android:layout_height="48dp"
- android:layout_marginLeft="3dp"
- android:layout_marginRight="3dp"
- android:layout_marginBottom="3dp"
- android:scaleType="fitCenter" />
- <ImageView android:id="@+id/target_badge"
- android:layout_width="16dp"
- android:layout_height="16dp"
- android:layout_gravity="end|bottom"
- android:visibility="gone"
- android:scaleType="fitCenter" />
- </FrameLayout>
+ <ImageView android:id="@+id/icon"
+ android:layout_width="@dimen/resolver_icon_size"
+ android:layout_height="@dimen/resolver_icon_size"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="3dp"
+ android:layout_marginBottom="3dp"
+ android:scaleType="fitCenter" />
<!-- Activity name -->
<TextView android:id="@android:id/text1"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6629b4c..6956a07 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2774,7 +2774,6 @@
<java-symbol type="layout" name="chooser_row" />
<java-symbol type="layout" name="chooser_row_direct_share" />
- <java-symbol type="id" name="target_badge" />
<java-symbol type="bool" name="config_supportDoubleTapWake" />
<java-symbol type="drawable" name="ic_perm_device_info" />
<java-symbol type="string" name="config_radio_access_family" />