Fix adaptive shortcut icons

Use adaptive icon for the static add contact shortcut.
Add O specific shortcut icon size.
Instead of adding padding after already getting the drawable from
the letter avatars, scale the drawable down by the same percentage
to get the padding. This eliminates the extra transparent part around
the icons, which led to the launcher filling in with black.

Test: manually verified on Nexus 5X with O
(https://screenshot.googleplex.com/Pa1t37DqeNJ)
and Pixel with O
(https://screenshot.googleplex.com/qujpyze1Euz)
and Nexus 6P emulator running N
(https://screenshot.googleplex.com/1KWQPx8RZFC)
and Samsung Galaxy S7 running M
(https://screenshot.googleplex.com/J39VES1jT8o)

Bug: 37922561
Change-Id: I6573f91e5441333eda05ec2640dc5137890be317
diff --git a/res/drawable-anydpi-v26/ic_add_contact_shortcut.xml b/res/drawable-anydpi-v26/ic_add_contact_shortcut.xml
new file mode 100644
index 0000000..fbd7a09
--- /dev/null
+++ b/res/drawable-anydpi-v26/ic_add_contact_shortcut.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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/ic_add_contact_shortcut_background"/>
+    <foreground android:drawable="@drawable/ic_add_circle_24dp"/>
+</adaptive-icon>
\ No newline at end of file
diff --git a/res/drawable/ic_add_circle_24dp.xml b/res/drawable/ic_add_circle_24dp.xml
index ae37470..ebaab34 100644
--- a/res/drawable/ic_add_circle_24dp.xml
+++ b/res/drawable/ic_add_circle_24dp.xml
@@ -20,7 +20,7 @@
         android:width="24dp"
         android:viewportHeight="192.0"
         android:viewportWidth="192.0">
-  <path android:fillColor="#F5F5F5"
+  <path android:fillColor="@color/ic_add_contact_shortcut_background"
         android:pathData="M96,8C47.38,8 8,47.38 8,96s39.38,88 88,88s88,-39.38 88,-88S144.62,8 96,8z"/>
   <path android:fillColor="#039BE5"
         android:pathData="M124,100h-24v24h-8v-24H68v-8h24V68h8v24h24V100z"/>
diff --git a/res/drawable/ic_add_contact_shortcut.xml b/res/drawable/ic_add_contact_shortcut.xml
new file mode 100644
index 0000000..85a7e61
--- /dev/null
+++ b/res/drawable/ic_add_contact_shortcut.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2016 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.
+-->
+
+<!-- A copy of ic_add_circle_24dp for use in pre O devices. -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="24dp"
+    android:width="24dp"
+    android:viewportHeight="192.0"
+    android:viewportWidth="192.0">
+    <path android:fillColor="@color/ic_add_contact_shortcut_background"
+        android:pathData="M96,8C47.38,8 8,47.38 8,96s39.38,88 88,88s88,-39.38 88,-88S144.62,8 96,8z"/>
+    <path android:fillColor="#039BE5"
+        android:pathData="M124,100h-24v24h-8v-24H68v-8h24V68h8v24h24V100z"/>
+</vector>
diff --git a/res/values-v26/dimens.xml b/res/values-v26/dimens.xml
new file mode 100644
index 0000000..b5737f2
--- /dev/null
+++ b/res/values-v26/dimens.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+    <!-- On Android O, shortcut icons will be 108dp unmasked.
+         See go/o-icons-eng for more details. -->
+    <dimen name="shortcut_icon_size">108dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 1aa84c6..e311ff3 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -16,6 +16,7 @@
 <resources>
     <!-- Adaptive icon background layer color -->
     <color name="ic_contacts_launcher_background">#2458CA</color>
+    <color name="ic_add_contact_shortcut_background">#F5F5F5</color>
 
     <!-- 87% black -->
     <color name="quantum_black_text">#dd000000</color>
diff --git a/res/xml/shortcuts.xml b/res/xml/shortcuts.xml
index c33893a..7ce90e6 100644
--- a/res/xml/shortcuts.xml
+++ b/res/xml/shortcuts.xml
@@ -17,7 +17,7 @@
 <shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
     <shortcut
         android:enabled="true"
-        android:icon="@drawable/ic_add_circle_24dp"
+        android:icon="@drawable/ic_add_contact_shortcut"
         android:shortcutId="shortcut-add-contact"
         android:shortcutShortLabel="@string/shortcut_add_contact">
         <intent
diff --git a/src/com/android/contacts/DynamicShortcuts.java b/src/com/android/contacts/DynamicShortcuts.java
index e287e4a..fc0d05a 100644
--- a/src/com/android/contacts/DynamicShortcuts.java
+++ b/src/com/android/contacts/DynamicShortcuts.java
@@ -16,6 +16,7 @@
 package com.android.contacts;
 
 import android.annotation.TargetApi;
+import android.app.ActivityManager;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
 import android.app.job.JobScheduler;
@@ -51,6 +52,7 @@
 
 import com.android.contacts.activities.RequestPermissionsActivity;
 import com.android.contacts.compat.CompatUtils;
+import com.android.contacts.lettertiles.LetterTileDrawable;
 import com.android.contacts.util.BitmapUtil;
 import com.android.contacts.util.ImplicitIntentsUtil;
 import com.android.contacts.util.PermissionsUtil;
@@ -95,21 +97,18 @@
     private static final int SHORTCUT_TYPE_CONTACT_URI = 1;
     private static final int SHORTCUT_TYPE_ACTION_URI = 2;
 
-    // The spec specifies that it should be 44dp @ xxxhdpi
-    // Note that ShortcutManager.getIconMaxWidth and ShortcutManager.getMaxHeight return different
-    // (larger) values.
-    private static final int RECOMMENDED_ICON_PIXEL_LENGTH = 176;
-
     @VisibleForTesting
     static final String[] PROJECTION = new String[] {
             Contacts._ID, Contacts.LOOKUP_KEY, Contacts.DISPLAY_NAME_PRIMARY
     };
 
     private final Context mContext;
+
     private final ContentResolver mContentResolver;
     private final ShortcutManager mShortcutManager;
     private int mShortLabelMaxLength = SHORT_LABEL_MAX_LENGTH;
     private int mLongLabelMaxLength = LONG_LABEL_MAX_LENGTH;
+    private int mIconSize;
     private final int mContentChangeMinUpdateDelay;
     private final int mContentChangeMaxUpdateDelay;
     private final JobScheduler mJobScheduler;
@@ -131,6 +130,12 @@
                 .getInteger(Experiments.DYNAMIC_MIN_CONTENT_CHANGE_UPDATE_DELAY_MILLIS);
         mContentChangeMaxUpdateDelay = Flags.getInstance()
                 .getInteger(Experiments.DYNAMIC_MAX_CONTENT_CHANGE_UPDATE_DELAY_MILLIS);
+        final ActivityManager am = (ActivityManager) context
+                .getSystemService(Context.ACTIVITY_SERVICE);
+        mIconSize = context.getResources().getDimensionPixelSize(R.dimen.shortcut_icon_size);
+        if (mIconSize == 0) {
+            mIconSize = am.getLauncherLargeIconSize();
+        }
     }
 
     @VisibleForTesting
@@ -373,10 +378,8 @@
         final int iconMaxHeight = mShortcutManager.getIconMaxHeight();
 
         final int sampleSize = Math.min(
-                BitmapUtil.findOptimalSampleSize(sourceWidth,
-                        RECOMMENDED_ICON_PIXEL_LENGTH),
-                BitmapUtil.findOptimalSampleSize(sourceHeight,
-                        RECOMMENDED_ICON_PIXEL_LENGTH));
+                BitmapUtil.findOptimalSampleSize(sourceWidth, mIconSize),
+                BitmapUtil.findOptimalSampleSize(sourceHeight, mIconSize));
         final BitmapFactory.Options opts = new BitmapFactory.Options();
         opts.inSampleSize = sampleSize;
 
@@ -404,46 +407,26 @@
             return BitmapUtil.getRoundedBitmap(bitmap, targetSize, targetSize);
         }
 
-        // If on O or higher, add padding around the bitmap.
-        final int paddingW = (int) (bitmap.getWidth() *
-                AdaptiveIconDrawable.getExtraInsetFraction());
-        final int paddingH = (int) (bitmap.getHeight() *
-                AdaptiveIconDrawable.getExtraInsetFraction());
-
-        final Bitmap scaledBitmap = Bitmap.createBitmap(bitmap.getWidth() + paddingW,
-                bitmap.getHeight() + paddingH, bitmap.getConfig());
-
-        final Canvas scaledCanvas = new Canvas(scaledBitmap);
-        scaledCanvas.drawBitmap(bitmap, paddingW / 2, paddingH / 2, null);
-
-        return scaledBitmap;
+        return bitmap;
     }
 
     private Bitmap getFallbackAvatar(String displayName, String lookupKey) {
-        final int width;
-        final int height;
-        final int padding;
-        if (BuildCompat.isAtLeastO()) {
-            // Add padding on >= O
-            padding = (int) (RECOMMENDED_ICON_PIXEL_LENGTH *
-                    AdaptiveIconDrawable.getExtraInsetFraction());
-            width = RECOMMENDED_ICON_PIXEL_LENGTH + padding;
-            height = RECOMMENDED_ICON_PIXEL_LENGTH + padding;
-        } else {
-            padding = 0;
-            width = RECOMMENDED_ICON_PIXEL_LENGTH;
-            height = RECOMMENDED_ICON_PIXEL_LENGTH;
-        }
+        // Use a circular icon if we're not on O or higher.
+        final boolean circularIcon = !BuildCompat.isAtLeastO();
 
         final ContactPhotoManager.DefaultImageRequest request =
-                new ContactPhotoManager.DefaultImageRequest(displayName, lookupKey, true);
+                new ContactPhotoManager.DefaultImageRequest(displayName, lookupKey, circularIcon);
+        if (BuildCompat.isAtLeastO()) {
+            // On O, scale the image down to add the padding needed by AdaptiveIcons.
+            request.scale = LetterTileDrawable.getAdaptiveIconScale();
+        }
         final Drawable avatar = ContactPhotoManager.getDefaultAvatarDrawableForContact(
                 mContext.getResources(), true, request);
-        final Bitmap result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        final Bitmap result = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
         // The avatar won't draw unless it thinks it is visible
         avatar.setVisible(true, true);
         final Canvas canvas = new Canvas(result);
-        avatar.setBounds(padding, padding, width - padding, height - padding);
+        avatar.setBounds(0, 0, mIconSize, mIconSize);
         avatar.draw(canvas);
         return result;
     }
diff --git a/src/com/android/contacts/ShortcutIntentBuilder.java b/src/com/android/contacts/ShortcutIntentBuilder.java
index 3a0b36b..0a4bb67 100644
--- a/src/com/android/contacts/ShortcutIntentBuilder.java
+++ b/src/com/android/contacts/ShortcutIntentBuilder.java
@@ -49,6 +49,7 @@
 import android.text.TextUtils.TruncateAt;
 
 import com.android.contacts.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.lettertiles.LetterTileDrawable;
 import com.android.contacts.util.BitmapUtil;
 import com.android.contacts.util.ImplicitIntentsUtil;
 
@@ -260,8 +261,14 @@
             Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
             return new BitmapDrawable(mContext.getResources(), bitmap);
         } else {
+            final DefaultImageRequest request = new DefaultImageRequest(displayName, lookupKey,
+                    false);
+            if (BuildCompat.isAtLeastO()) {
+                // On O, scale the image down to add the padding needed by AdaptiveIcons.
+                request.scale = LetterTileDrawable.getAdaptiveIconScale();
+            }
             return ContactPhotoManager.getDefaultAvatarDrawableForContact(mContext.getResources(),
-                    false, new DefaultImageRequest(displayName, lookupKey, false));
+                    false, request);
         }
     }
 
@@ -344,7 +351,6 @@
     }
 
     private Bitmap generateQuickContactIcon(Drawable photo) {
-
         // Setup the drawing classes
         Bitmap bitmap = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
         Canvas canvas = new Canvas(bitmap);
@@ -354,6 +360,11 @@
         photo.setBounds(dst);
         photo.draw(canvas);
 
+        // Don't put a rounded border on an icon for O
+        if (BuildCompat.isAtLeastO()) {
+            return bitmap;
+        }
+
         // Draw the icon with a rounded border
         RoundedBitmapDrawable roundedDrawable =
                 RoundedBitmapDrawableFactory.create(mResources, bitmap);
@@ -421,22 +432,24 @@
 
         // Draw the phone action icon as an overlay
         int iconWidth = icon.getWidth();
-        dst.set(iconWidth - ((int) (20 * density)), -1,
-                iconWidth, ((int) (19 * density)));
-        canvas.drawBitmap(phoneIcon, null, dst, photoPaint);
-
-        canvas.setBitmap(null);
-        if (!BuildCompat.isAtLeastO()) {
-            return icon;
+        if (BuildCompat.isAtLeastO()) {
+            // On O we need to calculate where the phone icon goes slightly differently. The whole
+            // canvas area is 108dp, a centered circle with a diameter of 66dp is the "safe zone".
+            // So we start the drawing the phone icon at
+            // 108dp - 21 dp (distance from right edge of safe zone to the edge of the canvas)
+            // - 24 dp (size of the phone icon) on the x axis (left)
+            // The y axis is simply 21dp for the distance to the safe zone (top).
+            // See go/o-icons-eng for more details and a handy picture.
+            final int left = (int) (mIconSize - (45 * density));
+            final int top = (int) (21 * density);
+            canvas.drawBitmap(phoneIcon, left, top, photoPaint);
+        } else {
+            dst.set(iconWidth - ((int) (20 * density)), -1,
+                    iconWidth, ((int) (19 * density)));
+            canvas.drawBitmap(phoneIcon, null, dst, photoPaint);
         }
 
-        // On >= O scale image up by AdaptiveIconDrawable.DEFAULT_VIEW_PORT_SCALE.
-        final int scale = (int) (icon.getHeight() *
-                (1f / (1 + 2 * AdaptiveIconDrawable.getExtraInsetFraction())));
-        final Bitmap scaledBitmap = Bitmap.createBitmap(icon.getWidth() + scale,
-                icon.getHeight() + scale, icon.getConfig());
-        Canvas scaledCanvas = new Canvas(scaledBitmap);
-        scaledCanvas.drawBitmap(icon, scale / 2, scale / 2, null);
-        return scaledBitmap;
+        canvas.setBitmap(null);
+        return icon;
     }
 }
diff --git a/src/com/android/contacts/lettertiles/LetterTileDrawable.java b/src/com/android/contacts/lettertiles/LetterTileDrawable.java
index 5652ac6..b80fd4f 100644
--- a/src/com/android/contacts/lettertiles/LetterTileDrawable.java
+++ b/src/com/android/contacts/lettertiles/LetterTileDrawable.java
@@ -26,6 +26,7 @@
 import android.graphics.Paint.Align;
 import android.graphics.Rect;
 import android.graphics.Typeface;
+import android.graphics.drawable.AdaptiveIconDrawable;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 
@@ -284,4 +285,11 @@
         mIsCircle = isCircle;
         return this;
     }
+
+    /**
+     * Returns the scale percentage as a float for LetterTileDrawables used in AdaptiveIcons.
+     */
+    public static float getAdaptiveIconScale() {
+        return 1 / (1 + (2 * AdaptiveIconDrawable.getExtraInsetFraction()));
+    }
 }