Adopt O shortcut changes for Intent.ACTION_CREATE_SHORTCUT

If we're on O, use ShortcutManager's .createShortcutResultIntent
and return that to the launcher.
Move ShortcutIntentBuilder out of list package since it doesn't
really make sense in there. Also allows us to keep DynamicShortcuts'
methods package private.

Test: Manually verify shortcuts created by long pressing on the
home screen (Contacts 1x1 widgets) are the newer app shortcuts on O
and are legacy shortcuts on pre-O (L, M, and N).

Bug: 36032908
Change-Id: Ic78c21daf223b59b45cbc98ceea2726fc29c055c
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index cd0b853..4adf6e7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -185,6 +185,8 @@
             android:uiOptions="splitActionBarWhenNarrow"
             android:windowSoftInputMode="adjustResize"
             android:visibleToInstantApps="true">
+            <meta-data android:name="android.app.shortcuts.new_config"
+                android:value="true" />
             <intent-filter>
                 <action android:name="android.intent.action.INSERT_OR_EDIT"/>
                 <category android:name="android.intent.category.DEFAULT"/>
diff --git a/src/com/android/contacts/DynamicShortcuts.java b/src/com/android/contacts/DynamicShortcuts.java
index 0901e56..087b421 100644
--- a/src/com/android/contacts/DynamicShortcuts.java
+++ b/src/com/android/contacts/DynamicShortcuts.java
@@ -275,26 +275,47 @@
                 .setDisabledMessage(mContext.getString(R.string.dynamic_shortcut_disabled_message))
                 .setExtras(extras);
 
-        if (displayName.length() < mLongLabelMaxLength) {
-            builder.setLongLabel(displayName);
-        } else {
-            builder.setLongLabel(displayName.substring(0, mLongLabelMaxLength - 1).trim() + "…");
-        }
-
-        if (displayName.length() < mShortLabelMaxLength) {
-            builder.setShortLabel(displayName);
-        } else {
-            builder.setShortLabel(displayName.substring(0, mShortLabelMaxLength - 1).trim() + "…");
-        }
+        setLabel(builder, displayName);
         return builder;
     }
 
+    @VisibleForTesting
+    ShortcutInfo getActionShortcutInfo(String id, String label, Intent action, Icon icon) {
+        if (id == null || label == null) {
+            return null;
+        }
+        final PersistableBundle extras = new PersistableBundle();
+        extras.putInt(EXTRA_SHORTCUT_TYPE, SHORTCUT_TYPE_CONTACT_URI);
+
+        final ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mContext, id)
+                .setIntent(action)
+                .setIcon(icon)
+                .setDisabledMessage(mContext.getString(R.string.dynamic_shortcut_disabled_message));
+
+        setLabel(builder, label);
+        return builder.build();
+    }
+
     public ShortcutInfo getQuickContactShortcutInfo(long id, String lookupKey, String displayName) {
         final ShortcutInfo.Builder builder = builderForContactShortcut(id, lookupKey, displayName);
         addIconForContact(id, lookupKey, displayName, builder);
         return builder.build();
     }
 
+    private void setLabel(ShortcutInfo.Builder builder, String label) {
+        if (label.length() < mLongLabelMaxLength) {
+            builder.setLongLabel(label);
+        } else {
+            builder.setLongLabel(label.substring(0, mLongLabelMaxLength - 1).trim() + "…");
+        }
+
+        if (label.length() < mShortLabelMaxLength) {
+            builder.setShortLabel(label);
+        } else {
+            builder.setShortLabel(label.substring(0, mShortLabelMaxLength - 1).trim() + "…");
+        }
+    }
+
     private void addIconForContact(Cursor cursor, ShortcutInfo.Builder builder) {
         final long id = cursor.getLong(0);
         final String lookupKey = cursor.getString(1);
diff --git a/src/com/android/contacts/list/ShortcutIntentBuilder.java b/src/com/android/contacts/ShortcutIntentBuilder.java
similarity index 78%
rename from src/com/android/contacts/list/ShortcutIntentBuilder.java
rename to src/com/android/contacts/ShortcutIntentBuilder.java
index 235e9f7..1019992 100644
--- a/src/com/android/contacts/list/ShortcutIntentBuilder.java
+++ b/src/com/android/contacts/ShortcutIntentBuilder.java
@@ -13,12 +13,14 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.contacts.list;
+package com.android.contacts;
 
 import android.app.ActivityManager;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.ShortcutInfo;
+import android.content.pm.ShortcutManager;
 import android.content.res.Resources;
 import android.database.Cursor;
 import android.graphics.Bitmap;
@@ -29,6 +31,7 @@
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
@@ -37,18 +40,20 @@
 import android.provider.ContactsContract.Data;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawable;
 import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory;
+import android.support.v4.os.BuildCompat;
 import android.telecom.PhoneAccount;
 import android.text.TextPaint;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 
-import com.android.contacts.ContactPhotoManager;
 import com.android.contacts.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.ContactsUtils;
-import com.android.contacts.R;
 import com.android.contacts.util.BitmapUtil;
 import com.android.contacts.util.ImplicitIntentsUtil;
 
+import com.google.common.collect.Lists;
+
+import java.util.List;
+
 /**
  * Constructs shortcut intents.
  */
@@ -264,22 +269,30 @@
 
     private void createContactShortcutIntent(Uri contactUri, String contentType, String displayName,
             String lookupKey, byte[] bitmapData) {
-        Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+        final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+        if (TextUtils.isEmpty(displayName)) {
+            displayName = mContext.getResources().getString(R.string.missing_name);
+        }
 
         final Intent shortcutIntent = ImplicitIntentsUtil.getIntentForQuickContactLauncherShortcut(
                 mContext, contactUri);
 
         final Bitmap icon = generateQuickContactIcon(drawable);
 
-        Intent intent = new Intent();
+        Intent intent = null;
+        if (BuildCompat.isAtLeastO()) {
+            final ShortcutManager sm = (ShortcutManager)
+                    mContext.getSystemService(Context.SHORTCUT_SERVICE);
+            final DynamicShortcuts dynamicShortcuts = new DynamicShortcuts(mContext);
+            final ShortcutInfo shortcutInfo = dynamicShortcuts.getActionShortcutInfo(
+                    lookupKey, displayName, shortcutIntent, Icon.createWithBitmap(icon));
+            intent = sm.createShortcutResultIntent(shortcutInfo);
+        }
+
+        intent = intent == null ? new Intent() : intent;
         intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
-        if (TextUtils.isEmpty(displayName)) {
-            intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, mContext.getResources().getString(
-                    R.string.missing_name));
-        } else {
-            intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
-        }
+        intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
 
         mListener.onShortcutIntentCreated(contactUri, intent);
     }
@@ -287,31 +300,42 @@
     private void createPhoneNumberShortcutIntent(Uri uri, String displayName, String lookupKey,
             byte[] bitmapData, String phoneNumber, int phoneType, String phoneLabel,
             String shortcutAction) {
-        Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
-
-        Bitmap bitmap;
-        Uri phoneUri;
-        if (Intent.ACTION_CALL.equals(shortcutAction)) {
-            // Make the URI a direct tel: URI so that it will always continue to work
-            phoneUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
-            bitmap = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
-                    R.drawable.quantum_ic_phone_vd_theme_24);
-        } else {
-            phoneUri = Uri.fromParts(ContactsUtils.SCHEME_SMSTO, phoneNumber, null);
-            bitmap = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
-                    R.drawable.quantum_ic_message_vd_theme_24);
-        }
-
-        Intent shortcutIntent = new Intent(shortcutAction, phoneUri);
-        shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-
-        Intent intent = new Intent();
-        intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
-        intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+        final Drawable drawable = getPhotoDrawable(bitmapData, displayName, lookupKey);
+        final Bitmap icon;
+        final Uri phoneUri;
 
         if (TextUtils.isEmpty(displayName)) {
             displayName = mContext.getResources().getString(R.string.missing_name);
         }
+
+        if (Intent.ACTION_CALL.equals(shortcutAction)) {
+            // Make the URI a direct tel: URI so that it will always continue to work
+            phoneUri = Uri.fromParts(PhoneAccount.SCHEME_TEL, phoneNumber, null);
+            icon = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
+                    R.drawable.quantum_ic_phone_vd_theme_24);
+        } else {
+            phoneUri = Uri.fromParts(ContactsUtils.SCHEME_SMSTO, phoneNumber, null);
+            icon = generatePhoneNumberIcon(drawable, phoneType, phoneLabel,
+                    R.drawable.quantum_ic_message_vd_theme_24);
+        }
+
+        final Intent shortcutIntent = new Intent(shortcutAction, phoneUri);
+        shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+        Intent intent = null;
+        if (BuildCompat.isAtLeastO()) {
+            final ShortcutManager sm = (ShortcutManager)
+                    mContext.getSystemService(Context.SHORTCUT_SERVICE);
+            final String id = shortcutAction + lookupKey;
+            final DynamicShortcuts dynamicShortcuts = new DynamicShortcuts(mContext);
+            final ShortcutInfo shortcutInfo = dynamicShortcuts.getActionShortcutInfo(
+                    id, displayName, shortcutIntent, Icon.createWithBitmap(icon));
+            intent = sm.createShortcutResultIntent(shortcutInfo);
+        }
+
+        intent = intent == null ? new Intent() : intent;
+        intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, icon);
+        intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
         if (TextUtils.equals(shortcutAction, Intent.ACTION_CALL)) {
             intent.putExtra(Intent.EXTRA_SHORTCUT_NAME,
                     mContext.getResources().getString(R.string.call_by_shortcut, displayName));
@@ -371,31 +395,36 @@
         photoPaint.setFilterBitmap(true);
         Rect dst = new Rect(0, 0, mIconSize, mIconSize);
 
-        // Create an overlay for the phone number type
-        CharSequence overlay = Phone.getTypeLabel(r, phoneType, phoneLabel);
+        // Create the overlay if we're pre-O. O created shortcuts have the app badge which overlaps
+        // the type overlay.
+        if (!BuildCompat.isAtLeastO()) {
+            // Create an overlay for the phone number type
+            CharSequence overlay = Phone.getTypeLabel(r, phoneType, phoneLabel);
 
-        if (overlay != null) {
-            TextPaint textPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
-            textPaint.setTextSize(r.getDimension(R.dimen.shortcut_overlay_text_size));
-            textPaint.setColor(r.getColor(R.color.textColorIconOverlay));
-            textPaint.setShadowLayer(4f, 0, 2f, r.getColor(R.color.textColorIconOverlayShadow));
+            if (overlay != null) {
+                TextPaint textPaint = new TextPaint(
+                        Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
+                textPaint.setTextSize(r.getDimension(R.dimen.shortcut_overlay_text_size));
+                textPaint.setColor(r.getColor(R.color.textColorIconOverlay));
+                textPaint.setShadowLayer(4f, 0, 2f, r.getColor(R.color.textColorIconOverlayShadow));
 
-            final FontMetricsInt fmi = textPaint.getFontMetricsInt();
+                final FontMetricsInt fmi = textPaint.getFontMetricsInt();
 
-            // First fill in a darker background around the text to be drawn
-            final Paint workPaint = new Paint();
-            workPaint.setColor(mOverlayTextBackgroundColor);
-            workPaint.setStyle(Paint.Style.FILL);
-            final int textPadding = r
-                    .getDimensionPixelOffset(R.dimen.shortcut_overlay_text_background_padding);
-            final int textBandHeight = (fmi.descent - fmi.ascent) + textPadding * 2;
-            dst.set(0, mIconSize - textBandHeight, mIconSize, mIconSize);
-            canvas.drawRect(dst, workPaint);
+                // First fill in a darker background around the text to be drawn
+                final Paint workPaint = new Paint();
+                workPaint.setColor(mOverlayTextBackgroundColor);
+                workPaint.setStyle(Paint.Style.FILL);
+                final int textPadding = r
+                        .getDimensionPixelOffset(R.dimen.shortcut_overlay_text_background_padding);
+                final int textBandHeight = (fmi.descent - fmi.ascent) + textPadding * 2;
+                dst.set(0, mIconSize - textBandHeight, mIconSize, mIconSize);
+                canvas.drawRect(dst, workPaint);
 
-            overlay = TextUtils.ellipsize(overlay, textPaint, mIconSize, TruncateAt.END);
-            final float textWidth = textPaint.measureText(overlay, 0, overlay.length());
-            canvas.drawText(overlay, 0, overlay.length(), (mIconSize - textWidth) / 2, mIconSize
-                    - fmi.descent - textPadding, textPaint);
+                overlay = TextUtils.ellipsize(overlay, textPaint, mIconSize, TruncateAt.END);
+                final float textWidth = textPaint.measureText(overlay, 0, overlay.length());
+                canvas.drawText(overlay, 0, overlay.length(), (mIconSize - textWidth) / 2, mIconSize
+                        - fmi.descent - textPadding, textPaint);
+            }
         }
 
         // Draw the phone action icon as an overlay
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
index e1f33b4..504828f 100644
--- a/src/com/android/contacts/list/ContactPickerFragment.java
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -24,7 +24,8 @@
 import android.widget.AdapterView;
 
 import com.android.contacts.R;
-import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+import com.android.contacts.ShortcutIntentBuilder;
+import com.android.contacts.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
 
 /**
  * Fragment for the contact list used for browsing contacts (as compared to
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
index 6a736db..ab78e0d 100644
--- a/src/com/android/contacts/list/PhoneNumberPickerFragment.java
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -28,7 +28,8 @@
 import android.view.ViewGroup;
 
 import com.android.contacts.R;
-import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+import com.android.contacts.ShortcutIntentBuilder;
+import com.android.contacts.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
 
 /**
  * Fragment containing a phone number list for picking.
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index e0986d5..014e1d5 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -126,8 +126,8 @@
 import com.android.contacts.interactions.SmsInteractionsLoader;
 import com.android.contacts.interactions.TouchPointManager;
 import com.android.contacts.lettertiles.LetterTileDrawable;
-import com.android.contacts.list.ShortcutIntentBuilder;
-import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
+import com.android.contacts.ShortcutIntentBuilder;
+import com.android.contacts.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
 import com.android.contacts.list.UiIntentActions;
 import com.android.contacts.logging.Logger;
 import com.android.contacts.logging.QuickContactEvent.ActionType;
diff --git a/src/com/android/contacts/util/ImplicitIntentsUtil.java b/src/com/android/contacts/util/ImplicitIntentsUtil.java
index 7465c96..0d00519 100644
--- a/src/com/android/contacts/util/ImplicitIntentsUtil.java
+++ b/src/com/android/contacts/util/ImplicitIntentsUtil.java
@@ -16,7 +16,7 @@
 
 package com.android.contacts.util;
 
-import static com.android.contacts.list.ShortcutIntentBuilder.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION;
+import static com.android.contacts.ShortcutIntentBuilder.INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION;
 
 import android.app.Activity;
 import android.content.Context;