16

With the new preview release yesterday I started to update my app to this version. Part of my app is to list installed applications with the associated icons.

Drawable drawable = mPackageManager.getApplicationIcon(packageName);

This part of the app still works. Apps like the clock, calculator or messages already support the new version of icons, so they return a AdaptiveIconDrawable.

Bitmap bitmap = ((BitmapDrawable) drawable).getBitmap();

This part doesn't work anymore. Is there any other way to convert AdaptiveIconDrawable to a Bitmap? Thanks in advance.

GoKo
  • 464
  • 1
  • 5
  • 11

2 Answers2

40

This is much more universal solution:

@NonNull
private Bitmap getBitmapFromDrawable(@NonNull Drawable drawable) {
    final Bitmap bmp = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
    final Canvas canvas = new Canvas(bmp);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);
    return bmp;
}

It will also render background in the system defined shape, if you don't want to use own.

You can use it on any Drawable, so also for VectorDrawable. It will works also on BitmapDrawable, just getBitmap() will be more memory efficient.

ATom
  • 15,960
  • 6
  • 46
  • 50
  • 2
    This seems like a better answer than going through the `LayerDrawable` rigmarole. You can also add `if(drawable instanceof BitmapDrawable) { return ((BitmapDrawable) drawable).getBitmap(); }` to the top of the function to optimize for the common case. – Steve Blackwell Sep 16 '17 at 07:39
  • 1
    This is great. Also, it is better to use `Math.max(drawable.getIntrinsicWidth(), 1)` or something because some drawables can return -1 if they don't have defined size, like colors. – Shchvova Sep 05 '19 at 20:45
9

We can achieve by following way,

AppIconHelper.java

public class AppIconHelper {

    public static Bitmap getAppIcon(PackageManager mPackageManager, String packageName) {

        if (Build.VERSION.SDK_INT >= 26) {
            return AppIconHelperV26.getAppIcon(mPackageManager, packageName);
        }

        try {
            Drawable drawable = mPackageManager.getApplicationIcon(packageName);

            return ((BitmapDrawable) drawable).getBitmap();
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }
}

For device supporting Android O( >=26) we can call AppIconHelperV26 class to get the AppIcon, In this, if its drawable is AdaptiveIconDrawable, we are doing the following steps 1. Get the Foreground and Background drawable and create as Layer Drawable 2. Using Canvas we can convert the Layer drawable to Bitmap

AppIconHelperV26.java

public class AppIconHelperV26 {

    @RequiresApi(api = Build.VERSION_CODES.O)
    public static Bitmap getAppIcon(PackageManager mPackageManager, String packageName) {

        try {
            Drawable drawable = mPackageManager.getApplicationIcon(packageName);

            if (drawable instanceof BitmapDrawable) {
                return ((BitmapDrawable) drawable).getBitmap();
            } else if (drawable instanceof AdaptiveIconDrawable) {
                Drawable backgroundDr = ((AdaptiveIconDrawable) drawable).getBackground();
                Drawable foregroundDr = ((AdaptiveIconDrawable) drawable).getForeground();

                Drawable[] drr = new Drawable[2];
                drr[0] = backgroundDr;
                drr[1] = foregroundDr;

                LayerDrawable layerDrawable = new LayerDrawable(drr);

                int width = layerDrawable.getIntrinsicWidth();
                int height = layerDrawable.getIntrinsicHeight();

                Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);

                Canvas canvas = new Canvas(bitmap);

                layerDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
                layerDrawable.draw(canvas);

                return bitmap;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        return null;
    }
}

Now you can call AppIconHelper.getAppIcon to get the Bitmap,

AppListAdapter.java

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    //... your code here
    Bitmap appIcon = AppIconHelper.getAppIcon(packageManager, packageName);
    imageView.setImageBitmap(appIcon);

}
Muthukrishnan Rajendran
  • 11,122
  • 3
  • 31
  • 41
  • 1
    Quite good solution it just doesn't round the corner of background as the AdaptiveIconDrawable itself does. – ATom Sep 02 '17 at 22:28