Merge "Extract the full telephone number from the tel: URL." into eclair-mr2
diff --git a/src/com/android/contacts/ContactsUtils.java b/src/com/android/contacts/ContactsUtils.java
index 24f683f..a277744 100644
--- a/src/com/android/contacts/ContactsUtils.java
+++ b/src/com/android/contacts/ContactsUtils.java
@@ -17,6 +17,9 @@
 package com.android.contacts;
 
 
+import com.android.contacts.model.ContactsSource;
+import com.android.contacts.util.Constants;
+
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
@@ -36,7 +39,6 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
-import android.provider.Im.ProviderNames;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
 import android.view.LayoutInflater;
@@ -45,9 +47,6 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import com.android.contacts.model.ContactsSource;
-import com.android.contacts.util.Constants;
-
 import java.util.ArrayList;
 
 public class ContactsUtils {
@@ -191,6 +190,19 @@
         return photoBm;
     }
 
+    // TODO find a proper place for the canonical version of these
+    public interface ProviderNames {
+        String YAHOO = "Yahoo";
+        String GTALK = "GTalk";
+        String MSN = "MSN";
+        String ICQ = "ICQ";
+        String AIM = "AIM";
+        String XMPP = "XMPP";
+        String JABBER = "JABBER";
+        String SKYPE = "SKYPE";
+        String QQ = "QQ";
+    }
+
     /**
      * This looks up the provider name defined in
      * {@link android.provider.Im.ProviderNames} from the predefined IM protocol id.
diff --git a/src/com/android/contacts/ViewContactActivity.java b/src/com/android/contacts/ViewContactActivity.java
index eb5aec5..2aeb34d 100644
--- a/src/com/android/contacts/ViewContactActivity.java
+++ b/src/com/android/contacts/ViewContactActivity.java
@@ -59,6 +59,7 @@
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
+import android.provider.ContactsContract.RawContactsEntity;
 import android.provider.ContactsContract.StatusUpdates;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Im;
@@ -297,31 +298,27 @@
         return null;
     }
 
-    // QUERY CODE //
     /** {@inheritDoc} */
-    public void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
-        try {
-            // Read incoming entities and consider binding
-            readEntities(iterator);
-            considerBindData();
-        } finally {
-            if (iterator != null) {
+    public void onQueryComplete(int token, Object cookie, Cursor cursor) {
+        if (token == TOKEN_STATUSES) {
+            try {
+                // Read available social rows and consider binding
+                readStatuses(cursor);
+            } finally {
+                if (cursor != null) {
+                    cursor.close();
+                }
+            }
+        } else {
+            EntityIterator iterator = RawContacts.newEntityIterator(cursor);
+            try {
+                // Read incoming entities and consider binding
+                readEntities(iterator);
+            } finally {
                 iterator.close();
             }
         }
-    }
-
-    /** {@inheritDoc} */
-    public void onQueryComplete(int token, Object cookie, Cursor cursor) {
-        try {
-            // Read available social rows and consider binding
-            readStatuses(cursor);
-            considerBindData();
-        } finally {
-            if (cursor != null) {
-                cursor.close();
-            }
-        }
+        considerBindData();
     }
 
     private long getRefreshedContactId() {
@@ -399,7 +396,7 @@
         mHasEntities = false;
         mHasStatuses = false;
 
-        mHandler.startQueryEntities(TOKEN_ENTITIES, null, RawContacts.CONTENT_URI,
+        mHandler.startQuery(TOKEN_ENTITIES, null, RawContactsEntity.CONTENT_URI, null,
                 RawContacts.CONTACT_ID + "=" + contactId, null, null);
         mHandler.startQuery(TOKEN_STATUSES, null, dataUri, StatusQuery.PROJECTION,
                         StatusUpdates.PRESENCE + " IS NOT NULL OR " + StatusUpdates.STATUS
diff --git a/src/com/android/contacts/model/EntitySet.java b/src/com/android/contacts/model/EntitySet.java
index be2f70f..ac53611 100644
--- a/src/com/android/contacts/model/EntitySet.java
+++ b/src/com/android/contacts/model/EntitySet.java
@@ -21,15 +21,13 @@
 import android.content.Entity;
 import android.content.EntityIterator;
 import android.content.ContentProviderOperation.Builder;
-import android.graphics.BitmapFactory;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
 import android.provider.ContactsContract.AggregationExceptions;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.RawContactsEntity;
 
 import com.google.android.collect.Lists;
 
@@ -65,26 +63,24 @@
      */
     public static EntitySet fromQuery(ContentResolver resolver, String selection,
             String[] selectionArgs, String sortOrder) {
-        EntityIterator iterator = null;
-        final EntitySet state = new EntitySet();
+        EntityIterator iterator = RawContacts.newEntityIterator(resolver.query(
+                RawContactsEntity.CONTENT_URI, null, selection, selectionArgs,
+                sortOrder));
         try {
+            final EntitySet state = new EntitySet();
             // Perform background query to pull contact details
-            iterator = resolver.queryEntities(RawContacts.CONTENT_URI, selection, selectionArgs,
-                    sortOrder);
             while (iterator.hasNext()) {
                 // Read all contacts into local deltas to prepare for edits
                 final Entity before = iterator.next();
                 final EntityDelta entity = EntityDelta.fromBefore(before);
                 state.add(entity);
             }
+            return state;
         } catch (RemoteException e) {
             throw new IllegalStateException("Problem querying contact details", e);
         } finally {
-            if (iterator != null) {
-                iterator.close();
-            }
+            iterator.close();
         }
-        return state;
     }
 
     /**
diff --git a/src/com/android/contacts/ui/DisplayGroupsActivity.java b/src/com/android/contacts/ui/DisplayGroupsActivity.java
index 9ac46ce..16af321 100644
--- a/src/com/android/contacts/ui/DisplayGroupsActivity.java
+++ b/src/com/android/contacts/ui/DisplayGroupsActivity.java
@@ -379,30 +379,29 @@
             mName = accountName;
             mType = accountType;
 
-            boolean hasGroups = false;
-
             final Uri groupsUri = Groups.CONTENT_URI.buildUpon()
                     .appendQueryParameter(Groups.ACCOUNT_NAME, accountName)
                     .appendQueryParameter(Groups.ACCOUNT_TYPE, accountType).build();
-            EntityIterator iterator = null;
+            EntityIterator iterator = ContactsContract.Groups.newEntityIterator(resolver.query(
+                    groupsUri, null, null, null, null));
             try {
+                boolean hasGroups = false;
+
                 // Create entries for each known group
-                iterator = resolver.queryEntities(groupsUri, null, null, null);
                 while (iterator.hasNext()) {
                     final ContentValues values = iterator.next().getEntityValues();
                     final GroupDelta group = GroupDelta.fromBefore(values);
                     addGroup(group);
                     hasGroups = true;
                 }
+                // Create single entry handling ungrouped status
+                mUngrouped = GroupDelta.fromSettings(resolver, accountName, accountType, hasGroups);
+                addGroup(mUngrouped);
             } catch (RemoteException e) {
                 Log.w(TAG, "Problem reading groups: " + e.toString());
             } finally {
-                if (iterator != null) iterator.close();
+                iterator.close();
             }
-
-            // Create single entry handling ungrouped status
-            mUngrouped = GroupDelta.fromSettings(resolver, accountName, accountType, hasGroups);
-            addGroup(mUngrouped);
         }
 
         /**
diff --git a/src/com/android/contacts/ui/QuickContactWindow.java b/src/com/android/contacts/ui/QuickContactWindow.java
index e1ff0e3..baf42ac 100644
--- a/src/com/android/contacts/ui/QuickContactWindow.java
+++ b/src/com/android/contacts/ui/QuickContactWindow.java
@@ -55,6 +55,8 @@
 import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.ContextThemeWrapper;
@@ -175,10 +177,17 @@
     private String[] mExcludeMimes;
 
     /**
-     * Specific MIME-types that should be bumped to the front of the dialog.
-     * Other MIME-types not appearing in this list follow in alphabetic order.
+     * {@link #PRECEDING_MIMETYPES} and {@link #FOLLOWING_MIMETYPES} are used to sort MIME-types.
+     *
+     * <p>The MIME-types in {@link #PRECEDING_MIMETYPES} appear in the front of the dialog,
+     * in the order in the array.
+     *
+     * <p>The ones in {@link #FOLLOWING_MIMETYPES} appear in the end of the dialog, in alphabetical
+     * order.
+     *
+     * <p>The rest go between them, in the order in the array.
      */
-    private static final String[] ORDERED_MIMETYPES = new String[] {
+    private static final String[] PRECEDING_MIMETYPES = new String[] {
             Phone.CONTENT_ITEM_TYPE,
             Contacts.CONTENT_ITEM_TYPE,
             Constants.MIME_SMS_ADDRESS,
@@ -186,6 +195,14 @@
     };
 
     /**
+     * See {@link #PRECEDING_MIMETYPES}.
+     */
+    private static final String[] FOLLOWING_MIMETYPES = new String[] {
+            StructuredPostal.CONTENT_ITEM_TYPE,
+            Website.CONTENT_ITEM_TYPE,
+    };
+
+    /**
      * Specific list {@link ApplicationInfo#packageName} of apps that are
      * prefered <strong>only</strong> for the purposes of default icons when
      * multiple {@link ResolveInfo} are found to match. This only happens when
@@ -197,7 +214,8 @@
             "com.android.calendar",
             "com.android.contacts",
             "com.android.mms",
-            "com.android.phone");
+            "com.android.phone",
+            "com.android.browser");
 
     private static final int TOKEN_DATA = 1;
 
@@ -741,6 +759,12 @@
                     mIntent = new Intent(Intent.ACTION_SENDTO, mailUri);
                 }
 
+            } else if (Website.CONTENT_ITEM_TYPE.equals(mimeType)) {
+                final String url = getAsString(cursor, Website.URL);
+                if (!TextUtils.isEmpty(url)) {
+                    mIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+                }
+
             } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType)) {
                 final boolean isEmail = Email.CONTENT_ITEM_TYPE.equals(
                         getAsString(cursor, Data.MIMETYPE));
@@ -1198,22 +1222,39 @@
             setHeaderText(R.id.timestamp, status.getTimestampLabel(mContext));
         }
 
-        // Turn our list of actions into UI elements, starting with common types
-        final Set<String> containedTypes = mActions.keySet();
-        for (String mimeType : ORDERED_MIMETYPES) {
+        // Turn our list of actions into UI elements
+
+        // Index where we start adding child views.
+        int index = mTrack.getChildCount() - 1;
+
+        // All the mime-types to add.
+        final Set<String> containedTypes = new HashSet<String>(mActions.keySet());
+
+        // First, add PRECEDING_MIMETYPES, which are most common.
+        for (String mimeType : PRECEDING_MIMETYPES) {
             if (containedTypes.contains(mimeType)) {
-                final int index = mTrack.getChildCount() - 1;
-                mTrack.addView(inflateAction(mimeType), index);
+                mTrack.addView(inflateAction(mimeType), index++);
                 containedTypes.remove(mimeType);
             }
         }
 
-        // Then continue with remaining MIME-types in alphabetical order
+        // Keep the current index to append non PRECEDING/FOLLOWING items.
+        final int indexAfterPreceding = index;
+
+        // Then, add FOLLOWING_MIMETYPES, which are least common.
+        for (String mimeType : FOLLOWING_MIMETYPES) {
+            if (containedTypes.contains(mimeType)) {
+                mTrack.addView(inflateAction(mimeType), index++);
+                containedTypes.remove(mimeType);
+            }
+        }
+
+        // Go back to just after PRECEDING_MIMETYPES, and append the rest.
+        index = indexAfterPreceding;
         final String[] remainingTypes = containedTypes.toArray(new String[containedTypes.size()]);
         Arrays.sort(remainingTypes);
         for (String mimeType : remainingTypes) {
-            final int index = mTrack.getChildCount() - 1;
-            mTrack.addView(inflateAction(mimeType), index);
+            mTrack.addView(inflateAction(mimeType), index++);
         }
     }
 
@@ -1554,11 +1595,6 @@
     }
 
     /** {@inheritDoc} */
-    public void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
-        // No actions
-    }
-
-    /** {@inheritDoc} */
     public void onAttachedToWindow() {
         // No actions
     }
diff --git a/src/com/android/contacts/ui/ShowOrCreateActivity.java b/src/com/android/contacts/ui/ShowOrCreateActivity.java
index 7728b36..0828b3f 100755
--- a/src/com/android/contacts/ui/ShowOrCreateActivity.java
+++ b/src/com/android/contacts/ui/ShowOrCreateActivity.java
@@ -221,11 +221,6 @@
 	return super.onCreateDialog(id);
     }
 
-    /** {@inheritDoc} */
-    public void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
-        // No actions
-    }
-
     /**
      * Listener for {@link DialogInterface} that launches a given {@link Intent}
      * when clicked. When clicked, this also closes the parent using
diff --git a/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java b/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
index 795ac79..83fae29 100644
--- a/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
+++ b/src/com/android/contacts/util/NotifyingAsyncQueryHandler.java
@@ -18,7 +18,6 @@
 
 import android.content.AsyncQueryHandler;
 import android.content.Context;
-import android.content.EntityIterator;
 import android.database.Cursor;
 
 import java.lang.ref.WeakReference;
@@ -26,7 +25,7 @@
 /**
  * Slightly more abstract {@link AsyncQueryHandler} that helps keep a
  * {@link WeakReference} back to a listener. Will properly close any
- * {@link Cursor} or {@link EntityIterator} if the listener ceases to exist.
+ * {@link Cursor} if the listener ceases to exist.
  * <p>
  * This pattern can be used to perform background queries without leaking
  * {@link Context} objects.
@@ -41,7 +40,6 @@
      */
     public interface AsyncQueryListener {
         void onQueryComplete(int token, Object cookie, Cursor cursor);
-        void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator);
     }
 
     public NotifyingAsyncQueryHandler(Context context, AsyncQueryListener listener) {
@@ -67,15 +65,4 @@
             cursor.close();
         }
     }
-
-    /** {@inheritDoc} */
-    @Override
-    protected void onQueryEntitiesComplete(int token, Object cookie, EntityIterator iterator) {
-        final AsyncQueryListener listener = mListener.get();
-        if (listener != null) {
-            listener.onQueryEntitiesComplete(token, cookie, iterator);
-        } else if (iterator != null) {
-            iterator.close();
-        }
-    }
 }