content: /*
   * Copyright (C) 2019 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.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;
  import android.content.res.Resources.Theme;
  import android.graphics.Bitmap;
  import android.graphics.BlurMaskFilter;
  import android.graphics.BlurMaskFilter.Blur;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Paint;
  import android.graphics.PaintFlagsDrawFilter;
  import android.graphics.PorterDuff;
  import android.graphics.PorterDuffXfermode;
  import android.graphics.Rect;
  import android.graphics.RectF;
  import android.graphics.drawable.AdaptiveIconDrawable;
  import android.graphics.drawable.BitmapDrawable;
  import android.graphics.drawable.ColorDrawable;
  import android.graphics.drawable.Drawable;
  import android.graphics.drawable.DrawableWrapper;
  import android.os.UserHandle;
  import android.util.AttributeSet;
  import android.util.Pools.SynchronizedPool;
  
  import com.android.internal.R;
  
  import org.xmlpull.v1.XmlPullParser;
  
  import java.nio.ByteBuffer;
  
  
  /**
   * @deprecated Use the Launcher3 Iconloaderlib at packages/apps/Launcher3/iconloaderlib. This class
   * is a temporary fork of Iconloader. It combines all necessary methods to render app icons that are
   * possibly badged. It is intended to be used only by Sharesheet for the Q release with custom code.
   */
  @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;
  
      private Context mContext;
      private Canvas mCanvas;
      private PackageManager mPm;
  
      private int mFillResIconDpi;
      private int mIconBitmapSize;
      private int mBadgeBitmapSize;
      private int mWrapperBackgroundColor;
  
      private Drawable mWrapperIcon;
      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
      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();
          mIconBitmapSize = iconBitmapSize;
          mBadgeBitmapSize = badgeBitmapSize;
          mFillResIconDpi = fillResIconDpi;
  
          mCanvas = new Canvas();
          mCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
  
          // Normalizer init
          // Use twice the icon size as maximum size to avoid scaling down twice.
          mMaxSize = iconBitmapSize * 2;
          mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8);
          mScaleCheckCanvas = new Canvas(mBitmap);
          mPixels = new byte[mMaxSize * mMaxSize];
          mLeftBorder = new float[mMaxSize];
          mRightBorder = new float[mMaxSize];
          mBounds = new Rect();
          mAdaptiveIconBounds = new Rect();
          mAdaptiveIconScale = SCALE_NOT_INITIALIZED;
  
          // Shadow generator init
          mDefaultBlurMaskFilter = new BlurMaskFilter(iconBitmapSize * BLUR_FACTOR,
                  Blur.NORMAL);
      }
  
      /**
       * Sets the background color used for wrapped adaptive icon
       *
       * @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
       */
      @Deprecated
      void setWrapperBackgroundColor(int color) {
          mWrapperBackgroundColor = (Color.alpha(color) < 255) ? DEFAULT_WRAPPER_BACKGROUND : color;
      }
  
      /**
       * Creates bitmap using the source drawable and various parameters.
       * The bitmap is visually normalized with other icons and has enough spacing to add shadow.
       * Note: this method has been modified from iconloaderlib to remove a profile diff check.
       *
       * @param icon                      source of the icon associated with a user that has no badge,
       *                                  likely user 0
       * @param user                      info can be used for a badge
       * @return a bitmap suitable for disaplaying as an icon at various system UIs.
       *
       * @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
       */
      @Deprecated
      Bitmap createUserBadgedIconBitmap(@Nullable Drawable icon, UserHandle user) {
          float [] scale = new float[1];
  
          // If no icon is provided use the system default
          if (icon == null) {
              icon = getFullResDefaultActivityIcon(mFillResIconDpi);
          }
          icon = normalizeAndWrapToAdaptiveIcon(icon, null, scale);
          Bitmap bitmap = createIconBitmap(icon, scale[0]);
          if (icon instanceof AdaptiveIconDrawable) {
              mCanvas.setBitmap(bitmap);
              recreateIcon(Bitmap.createBitmap(bitmap), mCanvas);
              mCanvas.setBitmap(null);
          }
  
          final Bitmap result;
          if (user != null /* if modification from iconloaderlib */) {
              BitmapDrawable drawable = new FixedSizeBitmapDrawable(bitmap);
              Drawable badged = mPm.getUserBadgedIcon(drawable, user);
              if (badged instanceof BitmapDrawable) {
                  result = ((BitmapDrawable) badged).getBitmap();
              } else {
                  result = createIconBitmap(badged, 1f);
              }
          } else {
              result = bitmap;
          }
  
          return result;
      }
  
      /**
       * Creates bitmap using the source drawable and flattened pre-rendered app icon.
       * The bitmap is visually normalized with other icons and has enough spacing to add shadow.
       * This is custom functionality added to Iconloaderlib that will need to be ported.
       *
       * @param icon                      source of the icon associated with a user that has no badge
       * @param renderedAppIcon           pre-rendered app icon to use as a badge, likely the output
       *                                  of createUserBadgedIconBitmap for user 0
       * @return a bitmap suitable for disaplaying as an icon at various system UIs.
       *
       * @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
       */
      @Deprecated
      Bitmap createAppBadgedIconBitmap(@Nullable Drawable icon, Bitmap renderedAppIcon) {
          // If no icon is provided use the system default
          if (icon == null) {
              icon = getFullResDefaultActivityIcon(mFillResIconDpi);
          }
  
          // Direct share icons cannot be adaptive, most will arrive as bitmaps. To get reliable
          // presentation, force all DS icons to be circular. Scale DS image so it completely fills.
          int w = icon.getIntrinsicWidth();
          int h = icon.getIntrinsicHeight();
          float scale = 1;
          if (h > w && w > 0) {
              scale = (float) h / w;
          } else if (w > h && h > 0) {
              scale = (float) w / h;
          }
          Bitmap bitmap = createIconBitmap(icon, scale);
          bitmap = maskBitmapToCircle(bitmap);
          icon = new BitmapDrawable(mContext.getResources(), bitmap);
  
          // We now have a circular masked and scaled icon, inset and apply shadow
          scale = getScale(icon, null);
          bitmap = createIconBitmap(icon, scale);
  
          mCanvas.setBitmap(bitmap);
          recreateIcon(Bitmap.createBitmap(bitmap), mCanvas);
  
          if (renderedAppIcon != null) {
              // Now scale down and apply the badge to the bottom right corner of the flattened icon
              renderedAppIcon = Bitmap.createScaledBitmap(renderedAppIcon, mBadgeBitmapSize,
                      mBadgeBitmapSize, false);
  
              // Paint the provided badge on top of the flattened icon
              mCanvas.drawBitmap(renderedAppIcon, mIconBitmapSize - mBadgeBitmapSize,
                      mIconBitmapSize - mBadgeBitmapSize, null);
          }
  
          mCanvas.setBitmap(null);
  
          return bitmap;
      }
  
      private Bitmap maskBitmapToCircle(Bitmap bitmap) {
          final Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
                  bitmap.getHeight(), Bitmap.Config.ARGB_8888);
          final Canvas canvas = new Canvas(output);
          final Paint paint = new Paint();
          paint.setAntiAlias(true);
  
          // Draw mask
          paint.setColor(0xffffffff);
          canvas.drawARGB(0, 0, 0, 0);
          canvas.drawCircle(bitmap.getWidth() / 2f,
                  bitmap.getHeight() / 2f,
                  bitmap.getWidth() / 2f - 1 /* -1 to avoid circles with flat sides */,
                  paint);
  
          // Draw masked bitmap
          paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
          final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
          canvas.drawBitmap(bitmap, rect, rect, paint);
  
          return output;
      }
  
      private static Drawable getFullResDefaultActivityIcon(int iconDpi) {
          return Resources.getSystem().getDrawableForDensity(android.R.mipmap.sym_def_app_icon,
                  iconDpi);
      }
  
      private Bitmap createIconBitmap(Drawable icon, float scale) {
          return createIconBitmap(icon, scale, mIconBitmapSize);
      }
  
      /**
       * @param icon drawable that should be flattened to a bitmap
       * @param scale the scale to apply before drawing {@param icon} on the canvas
       */
      private Bitmap createIconBitmap(Drawable icon, float scale, int size) {
          Bitmap bitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
  
          mCanvas.setBitmap(bitmap);
          mOldBounds.set(icon.getBounds());
  
          if (icon instanceof AdaptiveIconDrawable) {
              int offset = Math.max((int) Math.ceil(BLUR_FACTOR * size),
                      Math.round(size * (1 - scale) / 2));
              icon.setBounds(offset, offset, size - offset, size - offset);
              icon.draw(mCanvas);
          } else {
              if (icon instanceof BitmapDrawable) {
                  BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
                  Bitmap b = bitmapDrawable.getBitmap();
                  if (bitmap != null && b.getDensity() == Bitmap.DENSITY_NONE) {
                      bitmapDrawable.setTargetDensity(mContext.getResources().getDisplayMetrics());
                  }
              }
              int width = size;
              int height = size;
  
              int intrinsicWidth = icon.getIntrinsicWidth();
              int intrinsicHeight = icon.getIntrinsicHeight();
              if (intrinsicWidth > 0 && intrinsicHeight > 0) {
                  // Scale the icon proportionally to the icon dimensions
                  final float ratio = (float) intrinsicWidth / intrinsicHeight;
                  if (intrinsicWidth > intrinsicHeight) {
                      height = (int) (width / ratio);
                  } else if (intrinsicHeight > intrinsicWidth) {
                      width = (int) (height * ratio);
                  }
              }
              final int left = (size - width) / 2;
              final int top = (size - height) / 2;
              icon.setBounds(left, top, left + width, top + height);
              mCanvas.save();
              mCanvas.scale(scale, scale, size / 2, size / 2);
              icon.draw(mCanvas);
              mCanvas.restore();
  
          }
  
          icon.setBounds(mOldBounds);
          mCanvas.setBitmap(null);
          return bitmap;
      }
  
      private Drawable normalizeAndWrapToAdaptiveIcon(Drawable icon, RectF outIconBounds,
              float[] outScale) {
          float scale = 1f;
  
          if (mWrapperIcon == null) {
              mWrapperIcon = mContext.getDrawable(
                      R.drawable.iconfactory_adaptive_icon_drawable_wrapper).mutate();
          }
  
          AdaptiveIconDrawable dr = (AdaptiveIconDrawable) mWrapperIcon;
          dr.setBounds(0, 0, 1, 1);
          scale = getScale(icon, outIconBounds);
          if (!(icon instanceof AdaptiveIconDrawable)) {
              FixedScaleDrawable fsd = ((FixedScaleDrawable) dr.getForeground());
              fsd.setDrawable(icon);
              fsd.setScale(scale);
              icon = dr;
              scale = getScale(icon, outIconBounds);
  
              ((ColorDrawable) dr.getBackground()).setColor(mWrapperBackgroundColor);
          }
  
          outScale[0] = scale;
          return icon;
      }
  
  
      /* Normalization block */
  
      private static final float SCALE_NOT_INITIALIZED = 0;
      // Ratio of icon visible area to full icon size for a square shaped icon
      private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576;
      // Ratio of icon visible area to full icon size for a circular shaped icon
      private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576;
  
      private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4;
  
      // Slope used to calculate icon visible area to full icon size for any generic shaped icon.
      private static final float LINEAR_SCALE_SLOPE =
              (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT);
  
      private static final int MIN_VISIBLE_ALPHA = 40;
  
      private float mAdaptiveIconScale;
      private final Rect mAdaptiveIconBounds;
      private final Rect mBounds;
      private final int mMaxSize;
      private final byte[] mPixels;
      private final float[] mLeftBorder;
      private final float[] mRightBorder;
      private final Bitmap mBitmap;
      private final Canvas mScaleCheckCanvas;
  
      /**
       * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it
       * matches the design guidelines for a launcher icon.
       *
       * We first calculate the convex hull of the visible portion of the icon.
       * This hull then compared with the bounding rectangle of the hull to find how closely it
       * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not
       * an ideal solution but it gives satisfactory result without affecting the performance.
       *
       * This closeness is used to determine the ratio of hull area to the full icon size.
       * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR}
       *
       * @param outBounds optional rect to receive the fraction distance from each edge.
       */
      private synchronized float getScale(@NonNull Drawable d, @Nullable RectF outBounds) {
          if (d instanceof AdaptiveIconDrawable) {
              if (mAdaptiveIconScale != SCALE_NOT_INITIALIZED) {
                  if (outBounds != null) {
                      outBounds.set(mAdaptiveIconBounds);
                  }
                  return mAdaptiveIconScale;
              }
          }
          int width = d.getIntrinsicWidth();
          int height = d.getIntrinsicHeight();
          if (width <= 0 || height <= 0) {
              width = width <= 0 || width > mMaxSize ? mMaxSize : width;
              height = height <= 0 || height > mMaxSize ? mMaxSize : height;
          } else if (width > mMaxSize || height > mMaxSize) {
              int max = Math.max(width, height);
              width = mMaxSize * width / max;
              height = mMaxSize * height / max;
          }
  
          mBitmap.eraseColor(Color.TRANSPARENT);
          d.setBounds(0, 0, width, height);
          d.draw(mScaleCheckCanvas);
  
          ByteBuffer buffer = ByteBuffer.wrap(mPixels);
          buffer.rewind();
          mBitmap.copyPixelsToBuffer(buffer);
  
          // Overall bounds of the visible icon.
          int topY = -1;
          int bottomY = -1;
          int leftX = mMaxSize + 1;
          int rightX = -1;
  
          // Create border by going through all pixels one row at a time and for each row find
          // the first and the last non-transparent pixel. Set those values to mLeftBorder and
          // mRightBorder and use -1 if there are no visible pixel in the row.
  
          // buffer position
          int index = 0;
          // buffer shift after every row, width of buffer = mMaxSize
          int rowSizeDiff = mMaxSize - width;
          // first and last position for any row.
          int firstX, lastX;
  
          for (int y = 0; y < height; y++) {
              firstX = lastX = -1;
              for (int x = 0; x < width; x++) {
                  if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) {
                      if (firstX == -1) {
                          firstX = x;
                      }
                      lastX = x;
                  }
                  index++;
              }
              index += rowSizeDiff;
  
              mLeftBorder[y] = firstX;
              mRightBorder[y] = lastX;
  
              // If there is at least one visible pixel, update the overall bounds.
              if (firstX != -1) {
                  bottomY = y;
                  if (topY == -1) {
                      topY = y;
                  }
  
                  leftX = Math.min(leftX, firstX);
                  rightX = Math.max(rightX, lastX);
              }
          }
  
          if (topY == -1 || rightX == -1) {
              // No valid pixels found. Do not scale.
              return 1;
          }
  
          convertToConvexArray(mLeftBorder, 1, topY, bottomY);
          convertToConvexArray(mRightBorder, -1, topY, bottomY);
  
          // Area of the convex hull
          float area = 0;
          for (int y = 0; y < height; y++) {
              if (mLeftBorder[y] <= -1) {
                  continue;
              }
              area += mRightBorder[y] - mLeftBorder[y] + 1;
          }
  
          // Area of the rectangle required to fit the convex hull
          float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX);
          float hullByRect = area / rectArea;
  
          float scaleRequired;
          if (hullByRect < CIRCLE_AREA_BY_RECT) {
              scaleRequired = MAX_CIRCLE_AREA_FACTOR;
          } else {
              scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect);
          }
          mBounds.left = leftX;
          mBounds.right = rightX;
  
          mBounds.top = topY;
          mBounds.bottom = bottomY;
  
          if (outBounds != null) {
              outBounds.set(((float) mBounds.left) / width, ((float) mBounds.top) / height,
                      1 - ((float) mBounds.right) / width,
                      1 - ((float) mBounds.bottom) / height);
          }
          float areaScale = area / (width * height);
          // Use sqrt of the final ratio as the images is scaled across both width and height.
          float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1;
          if (d instanceof AdaptiveIconDrawable && mAdaptiveIconScale == SCALE_NOT_INITIALIZED) {
              mAdaptiveIconScale = scale;
              mAdaptiveIconBounds.set(mBounds);
          }
          return scale;
      }
  
      /**
       * Modifies {@param xCoordinates} to represent a convex border. Fills in all missing values
       * (except on either ends) with appropriate values.
       * @param xCoordinates map of x coordinate per y.
       * @param direction 1 for left border and -1 for right border.
       * @param topY the first Y position (inclusive) with a valid value.
       * @param bottomY the last Y position (inclusive) with a valid value.
       */
      private static void convertToConvexArray(
              float[] xCoordinates, int direction, int topY, int bottomY) {
          int total = xCoordinates.length;
          // The tangent at each pixel.
          float[] angles = new float[total - 1];
  
          int first = topY; // First valid y coordinate
          int last = -1;    // Last valid y coordinate which didn't have a missing value
  
          float lastAngle = Float.MAX_VALUE;
  
          for (int i = topY + 1; i <= bottomY; i++) {
              if (xCoordinates[i] <= -1) {
                  continue;
              }
              int start;
  
              if (lastAngle == Float.MAX_VALUE) {
                  start = first;
              } else {
                  float currentAngle = (xCoordinates[i] - xCoordinates[last]) / (i - last);
                  start = last;
                  // If this position creates a concave angle, keep moving up until we find a
                  // position which creates a convex angle.
                  if ((currentAngle - lastAngle) * direction < 0) {
                      while (start > first) {
                          start--;
                          currentAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start);
                          if ((currentAngle - angles[start]) * direction >= 0) {
                              break;
                          }
                      }
                  }
              }
  
              // Reset from last check
              lastAngle = (xCoordinates[i] - xCoordinates[start]) / (i - start);
              // Update all the points from start.
              for (int j = start; j < i; j++) {
                  angles[j] = lastAngle;
                  xCoordinates[j] = xCoordinates[start] + lastAngle * (j - start);
              }
              last = i;
          }
      }
  
      /* Shadow generator block */
  
      private static final float KEY_SHADOW_DISTANCE = 1f / 48;
      private static final int KEY_SHADOW_ALPHA = 61;
      private static final int AMBIENT_SHADOW_ALPHA = 30;
  
      private Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
      private Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
      private BlurMaskFilter mDefaultBlurMaskFilter;
  
      private synchronized void recreateIcon(Bitmap icon, Canvas out) {
          recreateIcon(icon, mDefaultBlurMaskFilter, AMBIENT_SHADOW_ALPHA, KEY_SHADOW_ALPHA, out);
      }
  
      private synchronized void recreateIcon(Bitmap icon, BlurMaskFilter blurMaskFilter,
              int ambientAlpha, int keyAlpha, Canvas out) {
          int[] offset = new int[2];
          mBlurPaint.setMaskFilter(blurMaskFilter);
          Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
  
          // Draw ambient shadow
          mDrawPaint.setAlpha(ambientAlpha);
          out.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);
  
          // Draw key shadow
          mDrawPaint.setAlpha(keyAlpha);
          out.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconBitmapSize,
                  mDrawPaint);
  
          // Draw the icon
          mDrawPaint.setAlpha(255); // TODO if b/128609682 not fixed by launch use .setAlpha(254)
          out.drawBitmap(icon, 0, 0, mDrawPaint);
      }
  
      /* Classes */
  
      /**
       * Extension of {@link DrawableWrapper} which scales the child drawables by a fixed amount.
       */
      public static class FixedScaleDrawable extends DrawableWrapper {
  
          private static final float LEGACY_ICON_SCALE = .7f * .6667f;
          private float mScaleX, mScaleY;
  
          public FixedScaleDrawable() {
              super(new ColorDrawable());
              mScaleX = LEGACY_ICON_SCALE;
              mScaleY = LEGACY_ICON_SCALE;
          }
  
          @Override
          public void draw(@NonNull Canvas canvas) {
              int saveCount = canvas.save();
              canvas.scale(mScaleX, mScaleY,
                      getBounds().exactCenterX(), getBounds().exactCenterY());
              super.draw(canvas);
              canvas.restoreToCount(saveCount);
          }
  
          @Override
          public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) { }
  
          @Override
          public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs, Theme theme) { }
  
          /**
           * Sets the scale associated with this drawable
           * @param scale
           */
          public void setScale(float scale) {
              float h = getIntrinsicHeight();
              float w = getIntrinsicWidth();
              mScaleX = scale * LEGACY_ICON_SCALE;
              mScaleY = scale * LEGACY_ICON_SCALE;
              if (h > w && w > 0) {
                  mScaleX *= w / h;
              } else if (w > h && h > 0) {
                  mScaleY *= h / w;
              }
          }
      }
  
      /**
       * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size.
       * This allows the badging to be done based on the action bitmap size rather than
       * the scaled bitmap size.
       */
      private static class FixedSizeBitmapDrawable extends BitmapDrawable {
  
          FixedSizeBitmapDrawable(Bitmap bitmap) {
              super(null, bitmap);
          }
  
          @Override
          public int getIntrinsicHeight() {
              return getBitmap().getWidth();
          }
  
          @Override
          public int getIntrinsicWidth() {
              return getBitmap().getWidth();
          }
      }
  
  }
  