Update resolver icon generation

Adds support for launcher-like icons via a ported form of
Iconloaderlib. Only fetches resolved activity's app icons with a special
cutout based on permission.

Bug: 126568207
Test: manual, mostly port of known to work code
Change-Id: Iba2c6f9aa5e63c457d9fd47921817c6529776b7a
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index c60a96b..c5fdc78 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -42,6 +42,8 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.AsyncTask;
@@ -49,6 +51,7 @@
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.PatternMatcher;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.StrictMode;
 import android.os.UserHandle;
@@ -56,7 +59,6 @@
 import android.provider.MediaStore;
 import android.provider.Settings;
 import android.text.TextUtils;
-import android.util.IconDrawableFactory;
 import android.util.Log;
 import android.util.Slog;
 import android.view.LayoutInflater;
@@ -131,7 +133,7 @@
     /** See {@link #setRetainInOnStop}. */
     private boolean mRetainInOnStop;
 
-    IconDrawableFactory mIconFactory;
+    SimpleIconFactory mSimpleIconFactory;
 
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override public void onSomePackagesChanged() {
@@ -309,7 +311,11 @@
         // as to mitigate Intent Capturing vulnerability
         mSupportsAlwaysUseOption = supportsAlwaysUseOption && !mUseLayoutForBrowsables;
 
-        mIconFactory = IconDrawableFactory.newInstance(this, true);
+        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;
         }
@@ -494,6 +500,7 @@
         }
     }
 
+    @Nullable
     Drawable getIcon(Resources res, int resId) {
         Drawable result;
         try {
@@ -505,26 +512,52 @@
         return result;
     }
 
+    /**
+     * Loads the icon for the provided ResolveInfo. 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;
-        try {
-            if (ri.resolvePackageName != null && ri.icon != 0) {
-                dr = getIcon(mPm.getResourcesForApplication(ri.resolvePackageName), ri.icon);
-                if (dr != null) {
-                    return mIconFactory.getShadowedIcon(dr);
+        Drawable dr = null;
+
+        // 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);
                 }
-            }
-            final int iconRes = ri.getIconResource();
-            if (iconRes != 0) {
-                dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName), iconRes);
-                if (dr != null) {
-                    return mIconFactory.getShadowedIcon(dr);
+                if (dr == null) {
+                    final int iconRes = ri.getIconResource();
+                    if (iconRes != 0) {
+                        dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.packageName),
+                                iconRes);
+                    }
                 }
+            } catch (NameNotFoundException e) {
+                Log.e(TAG, "SUBSTITUTE_SHARE_TARGET_APP_NAME_AND_ICON permission granted but "
+                        + "couldn't find resources for package", e);
             }
-        } catch (NameNotFoundException e) {
-            Log.e(TAG, "Couldn't find resources for package", e);
         }
-        return mIconFactory.getBadgedIcon(ri.activityInfo.applicationInfo);
+
+        // Use app icons for better user association
+        if (dr == null) {
+            try {
+                dr = getIcon(mPm.getResourcesForApplication(ri.activityInfo.applicationInfo),
+                        ri.activityInfo.applicationInfo.icon);
+            } catch (NameNotFoundException ignore) {
+            }
+        }
+
+        // Fall back to ApplicationInfo#loadIcon if nothing has been loaded
+        if (dr == null) {
+            dr = ri.activityInfo.applicationInfo.loadIcon(mPm);
+        }
+
+        return new BitmapDrawable(this.getResources(),
+                mSimpleIconFactory.createUserBadgedIconBitmap(dr, Process.myUserHandle()));
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/SimpleIconFactory.java b/core/java/com/android/internal/app/SimpleIconFactory.java
new file mode 100644
index 0000000..eb1530e
--- /dev/null
+++ b/core/java/com/android/internal/app/SimpleIconFactory.java
@@ -0,0 +1,609 @@
+/*
+ * 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.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+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.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.Process;
+import android.os.UserHandle;
+import android.util.AttributeSet;
+
+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.
+ */
+@Deprecated
+public class SimpleIconFactory {
+
+    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();
+
+    /**
+     * @deprecated Do not use, functionality will be replaced by iconloader lib eventually.
+     */
+    @Deprecated
+    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.
+     *
+     * @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 && !Process.myUserHandle().equals(user)) {
+            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.
+     *
+     * @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
+    public Bitmap createAppBadgedIconBitmap(@Nullable Drawable icon, Bitmap renderedAppIcon) {
+        // Flatten the passed in icon
+        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);
+        }
+
+        // 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.setBitmap(bitmap);
+        mCanvas.drawBitmap(renderedAppIcon, mIconBitmapSize - mBadgeBitmapSize,
+                mIconBitmapSize - mBadgeBitmapSize, null);
+        mCanvas.setBitmap(null);
+
+        return bitmap;
+    }
+
+    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();
+        }
+    }
+
+}
diff --git a/core/res/res/drawable/iconfactory_adaptive_icon_drawable_wrapper.xml b/core/res/res/drawable/iconfactory_adaptive_icon_drawable_wrapper.xml
new file mode 100644
index 0000000..3dd276d
--- /dev/null
+++ b/core/res/res/drawable/iconfactory_adaptive_icon_drawable_wrapper.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ 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
+  -->
+
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
+    <background android:drawable="@color/white"/>
+    <foreground>
+        <drawable
+            class="com.android.internal.app.SimpleIconFactory$FixedScaleDrawable"/>
+    </foreground>
+</adaptive-icon>
\ No newline at end of file
diff --git a/core/res/res/layout/resolve_list_item.xml b/core/res/res/layout/resolve_list_item.xml
index 5d52832..0bdb25a 100644
--- a/core/res/res/layout/resolve_list_item.xml
+++ b/core/res/res/layout/resolve_list_item.xml
@@ -29,8 +29,8 @@
     <!-- Activity icon when presenting dialog
          Size will be filled in by ResolverActivity -->
     <ImageView android:id="@+id/icon"
-               android:layout_width="24dp"
-               android:layout_height="24dp"
+               android:layout_width="@dimen/resolver_icon_size"
+               android:layout_height="@dimen/resolver_icon_size"
                android:layout_gravity="start|center_vertical"
                android:layout_marginStart="?attr/listPreferredItemPaddingStart"
                android:layout_marginTop="12dp"
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index fafd8fe..7134eed 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -723,4 +723,6 @@
     <dimen name="chooser_edge_margin_normal">24dp</dimen>
     <dimen name="chooser_preview_image_font_size">20sp</dimen>
     <dimen name="chooser_preview_width">-1px</dimen>
+    <dimen name="resolver_icon_size">42dp</dimen>
+    <dimen name="resolver_badge_size">18dp</dimen>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 503bbce..86f4489 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3702,4 +3702,9 @@
 
   <!-- For Auto-Brightness -->
   <java-symbol type="string" name="config_displayLightSensorType" />
+
+  <java-symbol type="drawable" name="iconfactory_adaptive_icon_drawable_wrapper"/>
+  <java-symbol type="dimen" name="resolver_icon_size"/>
+  <java-symbol type="dimen" name="resolver_badge_size"/>
+
 </resources>