Making contact shortcuts work again (except new contact).

Also turning shortcut creation into a background task.

Change-Id: I9f36bb9c824bce519a4852b728f480a036c42bb0
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 2c15f72..a8554b3 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -664,6 +664,8 @@
                     fragment.setCreateContactEnabled(true);
                 }
 
+                fragment.setShortcutRequested(mShortcutAction != null);
+
                 fragment.setOnContactPickerActionListener(new OnContactPickerActionListener() {
                     public void onSearchAllContactsAction(String string) {
                         doSearch();
@@ -675,10 +677,16 @@
                     }
 
                     public void onPickContactAction(Uri contactUri) {
+                        Intent intent = new Intent();
                         setResult(RESULT_OK, intent.setData(contactUri));
                         finish();
                     }
 
+                    public void onShortcutIntentCreated(Intent intent) {
+                        setResult(RESULT_OK, intent);
+                        finish();
+                    }
+
                     // TODO: finish action to support search in the picker
                 });
 
diff --git a/src/com/android/contacts/list/ContactPickerFragment.java b/src/com/android/contacts/list/ContactPickerFragment.java
index 62ac1c3..3cafa58 100644
--- a/src/com/android/contacts/list/ContactPickerFragment.java
+++ b/src/com/android/contacts/list/ContactPickerFragment.java
@@ -16,7 +16,10 @@
 package com.android.contacts.list;
 
 import com.android.contacts.R;
+import com.android.contacts.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
 
+import android.content.Intent;
+import android.net.Uri;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -26,21 +29,31 @@
  * Fragment for the contact list used for browsing contacts (as compared to
  * picking a contact with one of the PICK or SHORTCUT intents).
  */
-public class ContactPickerFragment extends ContactEntryListFragment<ContactListAdapter> {
+public class ContactPickerFragment extends ContactEntryListFragment<ContactListAdapter>
+        implements OnShortcutIntentCreatedListener {
 
     private OnContactPickerActionListener mListener;
     private boolean mCreateContactEnabled;
+    private boolean mShortcutRequested;
 
     public void setOnContactPickerActionListener(OnContactPickerActionListener listener) {
         mListener = listener;
     }
 
+    public boolean isCreateContactEnabled() {
+        return mCreateContactEnabled;
+    }
+
     public void setCreateContactEnabled(boolean flag) {
         this.mCreateContactEnabled = flag;
     }
 
-    public boolean isCreateContactEnabled() {
-        return mCreateContactEnabled;
+    public boolean isShortcutRequested() {
+        return mShortcutRequested;
+    }
+
+    public void setShortcutRequested(boolean flag) {
+        mShortcutRequested = flag;
     }
 
     @Override
@@ -72,13 +85,17 @@
         } else {
             ContactListAdapter adapter = getAdapter();
             adapter.moveToPosition(position);
-            mListener.onPickContactAction(adapter.getContactUri());
+            if (mShortcutRequested) {
+                ShortcutIntentBuilder builder = new ShortcutIntentBuilder(getActivity(), this);
+                builder.createContactShortcutIntent(adapter.getContactUri());
+            } else {
+                mListener.onPickContactAction(adapter.getContactUri());
+            }
         }
     }
 
     @Override
     protected ContactListAdapter createListAdapter() {
-        // TODO different adapter
         ContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
         adapter.setSectionHeaderDisplayEnabled(true);
 
@@ -92,8 +109,6 @@
         adapter.setContactNameDisplayOrder(getContactNameDisplayOrder());
         adapter.setSortOrder(getSortOrder());
 
-        // TODO more settings
-
         return adapter;
     }
 
@@ -107,4 +122,8 @@
             return inflater.inflate(R.layout.contacts_list_content, null);
         }
     }
+
+    public void onShortcutIntentCreated(Uri uri, Intent shortcutIntent) {
+        mListener.onShortcutIntentCreated(shortcutIntent);
+    }
 }
diff --git a/src/com/android/contacts/list/OnContactPickerActionListener.java b/src/com/android/contacts/list/OnContactPickerActionListener.java
index 38adf92..7245fbc 100644
--- a/src/com/android/contacts/list/OnContactPickerActionListener.java
+++ b/src/com/android/contacts/list/OnContactPickerActionListener.java
@@ -15,6 +15,7 @@
  */
 package com.android.contacts.list;
 
+import android.content.Intent;
 import android.net.Uri;
 
 /**
@@ -36,4 +37,9 @@
      * Returns the selected contact to the requester.
      */
     void onPickContactAction(Uri contactUri);
+
+    /**
+     * Returns the selected contact as a shortcut intent.
+     */
+    void onShortcutIntentCreated(Intent intent);
 }
diff --git a/src/com/android/contacts/list/ShortcutIntentBuilder.java b/src/com/android/contacts/list/ShortcutIntentBuilder.java
new file mode 100644
index 0000000..6452c05
--- /dev/null
+++ b/src/com/android/contacts/list/ShortcutIntentBuilder.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2010 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.contacts.list;
+
+import com.android.contacts.ContactsUtils;
+import com.android.contacts.R;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.Data;
+
+import java.util.Random;
+
+/**
+ * Constructs shortcut intents.
+ */
+public class ShortcutIntentBuilder {
+
+    private static final String[] CONTACT_COLUMNS = {
+        Contacts.DISPLAY_NAME,
+        Contacts.PHOTO_ID,
+    };
+
+    private static final int CONTACT_DISPLAY_NAME_COLUMN_INDEX = 0;
+    private static final int CONTACT_PHOTO_ID_COLUMN_INDEX = 1;
+
+    private static final String[] PHOTO_COLUMNS = {
+        Photo.PHOTO,
+    };
+
+    private static final int PHOTO_PHOTO_COLUMN_INDEX = 0;
+
+    private static final String PHOTO_SELECTION = Photo._ID + "=?";
+
+    private final OnShortcutIntentCreatedListener mListener;
+    private final Context mContext;
+    private final int mIconSize;
+
+    /**
+     * Listener interface.
+     */
+    public interface OnShortcutIntentCreatedListener {
+
+        /**
+         * Callback for shortcut intent creation.
+         *
+         * @param uri the original URI for which the shortcut intent has been
+         *            created.
+         * @param shortcutIntent resulting shortcut intent.
+         */
+        void onShortcutIntentCreated(Uri uri, Intent shortcutIntent);
+    }
+
+    public ShortcutIntentBuilder(Context context, OnShortcutIntentCreatedListener listener) {
+        mContext = context;
+        mListener = listener;
+
+        mIconSize = context.getResources().getDimensionPixelSize(android.R.dimen.app_icon_size);
+    }
+
+    public void createContactShortcutIntent(Uri contactUri) {
+        new ContactLoadingAsyncTask().execute(contactUri);
+    }
+
+    /**
+     * An asynchronous task that loads the photo from the database.
+     */
+    private final class ContactLoadingAsyncTask extends AsyncTask<Uri, Void, Void> {
+        private Uri mUri;
+        private String mDisplayName;
+        private byte[] mBitmapData;
+
+        @Override
+        protected Void doInBackground(Uri... uris) {
+            mUri = uris[0];
+
+            ContentResolver resolver = mContext.getContentResolver();
+
+            long photoId = 0;
+
+            Cursor cursor = resolver.query(mUri, CONTACT_COLUMNS, null, null, null);
+            if (cursor != null) {
+                try {
+                    if (cursor.moveToFirst()) {
+                        mDisplayName = cursor.getString(CONTACT_DISPLAY_NAME_COLUMN_INDEX);
+                        photoId = cursor.getLong(CONTACT_PHOTO_ID_COLUMN_INDEX);
+                    }
+                } finally {
+                    cursor.close();
+                }
+            }
+
+            if (photoId != 0) {
+                cursor = resolver.query(Data.CONTENT_URI, PHOTO_COLUMNS, PHOTO_SELECTION,
+                        new String[] { String.valueOf(photoId) }, null);
+                if (cursor != null) {
+                    try {
+                        if (cursor.moveToFirst()) {
+                            mBitmapData = cursor.getBlob(PHOTO_PHOTO_COLUMN_INDEX);
+                        }
+                    } finally {
+                        cursor.close();
+                    }
+                }
+            }
+
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            createContactShortcutIntent(mUri, mDisplayName, mBitmapData);
+        }
+    }
+
+    public void createContactShortcutIntent(Uri contactUri, String displayName, byte[] bitmapData) {
+
+        Bitmap bitmap;
+
+        if (bitmapData != null) {
+            bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length, null);
+        } else {
+            final int[] fallbacks = {
+                R.drawable.ic_contact_picture,
+                R.drawable.ic_contact_picture_2,
+                R.drawable.ic_contact_picture_3
+            };
+            bitmap = BitmapFactory.decodeResource(mContext.getResources(),
+                    fallbacks[new Random().nextInt(fallbacks.length)]);
+        }
+
+        Intent shortcutIntent;
+        // This is a simple shortcut to view a contact.
+        shortcutIntent = new Intent(ContactsContract.QuickContact.ACTION_QUICK_CONTACT);
+        shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+
+        shortcutIntent.setData(contactUri);
+        shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_MODE,
+                ContactsContract.QuickContact.MODE_LARGE);
+        shortcutIntent.putExtra(ContactsContract.QuickContact.EXTRA_EXCLUDE_MIMES,
+                (String[]) null);
+        shortcutIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+
+        Intent intent = new Intent();
+
+        final Bitmap icon = framePhoto(bitmap);
+        intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, scaleToAppIconSize(icon));
+        intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
+        intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, displayName);
+
+        mListener.onShortcutIntentCreated(contactUri, intent);
+    }
+
+    private Bitmap framePhoto(Bitmap photo) {
+        final Resources r = mContext.getResources();
+        final Drawable frame = r.getDrawable(com.android.internal.R.drawable.quickcontact_badge);
+
+        final int width = r.getDimensionPixelSize(R.dimen.contact_shortcut_frame_width);
+        final int height = r.getDimensionPixelSize(R.dimen.contact_shortcut_frame_height);
+
+        frame.setBounds(0, 0, width, height);
+
+        final Rect padding = new Rect();
+        frame.getPadding(padding);
+
+        final Rect source = new Rect(0, 0, photo.getWidth(), photo.getHeight());
+        final Rect destination = new Rect(padding.left, padding.top,
+                width - padding.right, height - padding.bottom);
+
+        final int d = Math.max(width, height);
+        final Bitmap b = Bitmap.createBitmap(d, d, Bitmap.Config.ARGB_8888);
+        final Canvas c = new Canvas(b);
+
+        c.translate((d - width) / 2.0f, (d - height) / 2.0f);
+        frame.draw(c);
+        c.drawBitmap(photo, source, destination, new Paint(Paint.FILTER_BITMAP_FLAG));
+
+        return b;
+    }
+
+    private Bitmap scaleToAppIconSize(Bitmap photo) {
+
+        // Setup the drawing classes
+        Bitmap icon = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
+        Canvas canvas = new Canvas(icon);
+
+        // Copy in the photo
+        Paint photoPaint = new Paint();
+        photoPaint.setDither(true);
+        photoPaint.setFilterBitmap(true);
+        Rect src = new Rect(0,0, photo.getWidth(),photo.getHeight());
+        Rect dst = new Rect(0,0, mIconSize, mIconSize);
+        canvas.drawBitmap(photo, src, dst, photoPaint);
+
+        return icon;
+    }
+}