Merge "Revert "Remove strict mode violation with number formatting.""
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 18782b9..219ff67 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -29,6 +29,7 @@
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
     <uses-permission android:name="android.permission.READ_PROFILE" />
     <uses-permission android:name="android.permission.WRITE_PROFILE" />
+    <uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.NFC" />
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f4b94af..e33debb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1811,7 +1811,15 @@
     <!-- Button label to prompt the user to add another account (when there are already existing accounts on the device) [CHAR LIMIT=30] -->
     <string name="add_new_account">Add new account</string>
 
-    <!-- Dialog message which is shown when the user tries to make a phone call
-         to prohibited phone numbers [CHAR LIMIT=NONE] -->
-    <string name="phone_call_prohibited" msgid="4313552620858880999">Call not sent.</string>
+    <!-- Dialog title which is shown when the user tries to make a phone call
+         to prohibited phone numbers [CHAR LIMIT=40] -->
+    <string name="dialog_phone_call_prohibited_title" msgid="4313552620858880999">Call not sent</string>
+
+    <!-- Dialog title which is shown when the user tries to check voicemail
+         while the system isn't ready for the access. [CHAR LIMIT=40] -->
+    <string name="dialog_voicemail_not_ready_title">Voicemail number unavailable</string>
+
+    <!-- Dialog message which is shown when the user tries to check voicemail
+         while the system isn't ready for the access. [CHAR LIMIT=NONE] -->
+    <string name="dialog_voicemail_not_ready_message">To configure voicemail, go to Menu &gt; Settings.</string>
 </resources>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index e3de72b..5e7d9e2 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -46,7 +46,7 @@
         <item name="list_item_header_underline_color">@color/people_app_theme_color</item>
         <item name="contact_browser_list_padding_left">16dip</item>
         <item name="contact_browser_list_padding_right">0dip</item>
-        <item name="contact_browser_background">@android:color/black</item>
+        <item name="contact_browser_background">@android:color/transparent</item>
         <item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
         <!-- CallLog -->
         <item name="call_log_primary_text_color">#FFFFFF</item>
diff --git a/res/xml/searchable.xml b/res/xml/searchable.xml
index 17ef37f..949ec36 100644
--- a/res/xml/searchable.xml
+++ b/res/xml/searchable.xml
@@ -25,6 +25,7 @@
     android:searchMode="queryRewriteFromText"
 
     android:includeInGlobalSearch="true"
+    android:queryAfterZeroResults="true"
     android:searchSuggestAuthority="com.android.contacts"
     android:searchSuggestIntentAction="android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"
     android:searchSuggestIntentData="content://com.android.contacts/contacts/lookup"
diff --git a/src/com/android/contacts/CallDetailActivity.java b/src/com/android/contacts/CallDetailActivity.java
index 8d7561a..113a18b 100644
--- a/src/com/android/contacts/CallDetailActivity.java
+++ b/src/com/android/contacts/CallDetailActivity.java
@@ -96,6 +96,7 @@
     private PhoneNumberHelper mPhoneNumberHelper;
     private PhoneCallDetailsHelper mPhoneCallDetailsHelper;
     private TextView mHeaderTextView;
+    private View mHeaderOverlayView;
     private ImageView mMainActionView;
     private ImageButton mMainActionPushLayerView;
     private ImageView mContactBackgroundView;
@@ -240,6 +241,7 @@
         mVoicemailStatusHelper = new VoicemailStatusHelperImpl();
         mAsyncQueryHandler = new CallDetailActivityQueryHandler(this);
         mHeaderTextView = (TextView) findViewById(R.id.header_text);
+        mHeaderOverlayView = findViewById(R.id.photo_text_bar);
         mStatusMessageView = findViewById(R.id.voicemail_status);
         mStatusMessageText = (TextView) findViewById(R.id.voicemail_status_message);
         mStatusMessageAction = (TextView) findViewById(R.id.voicemail_status_action);
@@ -454,6 +456,8 @@
                 if (mainActionIntent == null) {
                     mMainActionView.setVisibility(View.INVISIBLE);
                     mMainActionPushLayerView.setVisibility(View.GONE);
+                    mHeaderTextView.setVisibility(View.INVISIBLE);
+                    mHeaderOverlayView.setVisibility(View.INVISIBLE);
                 } else {
                     mMainActionView.setVisibility(View.VISIBLE);
                     mMainActionView.setImageResource(mainActionIcon);
@@ -465,6 +469,8 @@
                         }
                     });
                     mMainActionPushLayerView.setContentDescription(mainActionDescription);
+                    mHeaderTextView.setVisibility(View.VISIBLE);
+                    mHeaderOverlayView.setVisibility(View.VISIBLE);
                 }
 
                 // This action allows to call the number that places the call.
@@ -511,19 +517,27 @@
                                 findViewById(R.id.controls)));
                 BackScrollManager.bind(
                         new ScrollableHeader() {
-                            private View controls = findViewById(R.id.controls);
-                            private View photo = findViewById(R.id.contact_background_sizer);
-                            private View nameHeader = findViewById(R.id.photo_text_bar);
+                            private View mControls = findViewById(R.id.controls);
+                            private View mPhoto = findViewById(R.id.contact_background_sizer);
+                            private View mHeader = findViewById(R.id.photo_text_bar);
+                            private View mSeparator = findViewById(R.id.blue_separator);
 
                             @Override
                             public void setOffset(int offset) {
-                                controls.setY(-offset);
+                                mControls.setY(-offset);
                             }
 
                             @Override
                             public int getMaximumScrollableHeaderOffset() {
-                                // We can scroll the photo out, but we should keep the header.
-                                return photo.getHeight() - nameHeader.getHeight();
+                                // We can scroll the photo out, but we should keep the header if
+                                // present.
+                                if (mHeader.getVisibility() == View.VISIBLE) {
+                                    return mPhoto.getHeight() - mHeader.getHeight();
+                                } else {
+                                    // If the header is not present, we should also scroll out the
+                                    // separator line.
+                                    return mPhoto.getHeight() + mSeparator.getHeight();
+                                }
                             }
                         },
                         historyList);
@@ -720,7 +734,6 @@
 
     private StatusMessage getStatusMessage(Cursor statusCursor) {
         List<StatusMessage> messages = mVoicemailStatusHelper.getStatusMessages(statusCursor);
-        Log.d(TAG, "Num status messages: " + messages.size());
         if (messages.size() == 0) {
             return null;
         }
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index 57defcf..007c1e0 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -73,8 +73,8 @@
 public class ContactLoader extends Loader<ContactLoader.Result> {
     private static final String TAG = "ContactLoader";
 
-    private Uri mLookupUri;
     private final Uri mRequestedUri;
+    private Uri mLookupUri;
     private boolean mLoadGroupMetaData;
     private boolean mLoadStreamItems;
     private final boolean mLoadInvitableAccountTypes;
@@ -91,10 +91,14 @@
      * The result of a load operation. Contains all data necessary to display the contact.
      */
     public static final class Result {
-        /**
-         * Singleton instance that represents "No Contact Found"
-         */
-        public static final Result NOT_FOUND = new Result((Exception) null);
+        private enum Status {
+            /** Contact is successfully loaded */
+            LOADED,
+            /** There was an error loading the contact */
+            ERROR,
+            /** Contact is not found */
+            NOT_FOUND,
+        }
 
         private final Uri mRequestedUri;
         private final Uri mLookupUri;
@@ -130,13 +134,19 @@
         private final String mCustomRingtone;
         private final boolean mIsUserProfile;
 
+        private final Status mStatus;
         private final Exception mException;
 
         /**
          * Constructor for special results, namely "no contact found" and "error".
          */
-        private Result(Exception exception) {
-            mRequestedUri = null;
+        private Result(Uri requestedUri, Status status, Exception exception) {
+            if (status == Status.ERROR && exception == null) {
+                throw new IllegalArgumentException("ERROR result must have exception");
+            }
+            mStatus = status;
+            mException = exception;
+            mRequestedUri = requestedUri;
             mLookupUri = null;
             mUri = null;
             mDirectoryId = -1;
@@ -158,11 +168,14 @@
             mSendToVoicemail = false;
             mCustomRingtone = null;
             mIsUserProfile = false;
-            mException = exception;
         }
 
-        private static Result forError(Exception exception) {
-            return new Result(exception);
+        private static Result forError(Uri requestedUri, Exception exception) {
+            return new Result(requestedUri, Status.ERROR, exception);
+        }
+
+        private static Result forNotFound(Uri requestedUri) {
+            return new Result(requestedUri, Status.NOT_FOUND, null);
         }
 
         /**
@@ -173,6 +186,7 @@
                 String photoUri, String displayName, String altDisplayName, String phoneticName,
                 boolean starred, Integer presence, boolean sendToVoicemail, String customRingtone,
                 boolean isUserProfile) {
+            mStatus = Status.LOADED;
             mException = null;
             mRequestedUri = requestedUri;
             mLookupUri = lookupUri;
@@ -199,6 +213,7 @@
         }
 
         private Result(Result from) {
+            mStatus = from.mStatus;
             mException = from.mException;
             mRequestedUri = from.mRequestedUri;
             mLookupUri = from.mLookupUri;
@@ -293,15 +308,36 @@
         /**
          * @return true when an exception happened during loading, in which case
          *     {@link #getException} returns the actual exception object.
+         *     Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
+         *     {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
+         *     and vice versa.
          */
         public boolean isError() {
-            return mException != null;
+            return mStatus == Status.ERROR;
         }
 
         public Exception getException() {
             return mException;
         }
 
+        /**
+         * @return true when the specified contact is not found.
+         *     Note {@link #isNotFound()} and {@link #isError()} are mutually exclusive; If
+         *     {@link #isError()} is {@code true}, {@link #isNotFound()} is always {@code false},
+         *     and vice versa.
+         */
+        public boolean isNotFound() {
+            return mStatus == Status.NOT_FOUND;
+        }
+
+        /**
+         * @return true if the specified contact is successfully loaded.
+         *     i.e. neither {@link #isError()} nor {@link #isNotFound()}.
+         */
+        public boolean isLoaded() {
+            return mStatus == Status.LOADED;
+        }
+
         public long getNameRawContactId() {
             return mNameRawContactId;
         }
@@ -647,7 +683,7 @@
                 final ContentResolver resolver = getContext().getContentResolver();
                 final Uri uriCurrentFormat = ensureIsContactUri(resolver, mLookupUri);
                 Result result = loadContactEntity(resolver, uriCurrentFormat);
-                if (result != Result.NOT_FOUND) {
+                if (!result.isNotFound()) {
                     if (result.isDirectoryEntry()) {
                         loadDirectoryMetaData(result);
                     } else if (mLoadGroupMetaData) {
@@ -666,7 +702,7 @@
                 return result;
             } catch (Exception e) {
                 Log.e(TAG, "Error loading the contact: " + mLookupUri, e);
-                return Result.forError(e);
+                return Result.forError(mRequestedUri, e);
             }
         }
 
@@ -717,13 +753,13 @@
                     Contacts.Entity.RAW_CONTACT_ID);
             if (cursor == null) {
                 Log.e(TAG, "No cursor returned in loadContactEntity");
-                return Result.NOT_FOUND;
+                return Result.forNotFound(mRequestedUri);
             }
 
             try {
                 if (!cursor.moveToFirst()) {
                     cursor.close();
-                    return Result.NOT_FOUND;
+                    return Result.forNotFound(mRequestedUri);
                 }
 
                 long currentRawContactId = -1;
@@ -1143,7 +1179,7 @@
 
             mContact = result;
 
-            if (!result.isError() && result != Result.NOT_FOUND) {
+            if (result.isLoaded()) {
                 mLookupUri = result.getLookupUri();
 
                 if (!result.isDirectoryEntry()) {
diff --git a/src/com/android/contacts/calllog/CallLogAdapter.java b/src/com/android/contacts/calllog/CallLogAdapter.java
index 0bbf53c..d54a47e 100644
--- a/src/com/android/contacts/calllog/CallLogAdapter.java
+++ b/src/com/android/contacts/calllog/CallLogAdapter.java
@@ -400,7 +400,15 @@
         // Determine the contact info.
         if (PhoneNumberUtils.isUriNumber(number)) {
             // This "number" is really a SIP address.
-            info = queryContactInfoForSipAddress(number);
+            ContactInfo sipInfo = queryContactInfoForSipAddress(number);
+            if (sipInfo == null || sipInfo == ContactInfo.EMPTY) {
+                // Check whether the username is actually a phone number of contact.
+                String username = number.substring(0, number.indexOf('@'));
+                if (PhoneNumberUtils.isGlobalPhoneNumber(username)) {
+                    sipInfo = queryContactInfoForPhoneNumber(username);
+                }
+            }
+            info = sipInfo;
         } else {
             info = queryContactInfoForPhoneNumber(number);
         }
diff --git a/src/com/android/contacts/calllog/CallLogFragment.java b/src/com/android/contacts/calllog/CallLogFragment.java
index 0628db4..c9a4b5b 100644
--- a/src/com/android/contacts/calllog/CallLogFragment.java
+++ b/src/com/android/contacts/calllog/CallLogFragment.java
@@ -121,7 +121,6 @@
         updateVoicemailStatusMessage(statusCursor);
 
         int activeSources = mVoicemailStatusHelper.getNumberActivityVoicemailSources(statusCursor);
-        Log.d(TAG, "Num active sources: " + activeSources);
         setVoicemailSourcesAvailable(activeSources != 0);
         MoreCloseables.closeQuietly(statusCursor);
     }
diff --git a/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java b/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java
index f795a9c..c4fb36c 100644
--- a/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java
@@ -161,14 +161,12 @@
         final Intent contentIntent;
         if (newCalls.length == 1) {
             // Open the voicemail directly.
-            Log.d(TAG, "Opening voicemail directly on select");
             contentIntent = new Intent(mContext, CallDetailActivity.class);
             contentIntent.setData(newCalls[0].callsUri);
             contentIntent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI,
                     newCalls[0].voicemailUri);
         } else {
             // Open the call log.
-            Log.d(TAG, "Opening call log on select");
             contentIntent = new Intent(Intent.ACTION_VIEW, Calls.CONTENT_URI);
         }
         notification.contentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);
@@ -250,7 +248,6 @@
                 while (cursor.moveToNext()) {
                     newCalls[cursor.getPosition()] = createNewCallsFromCursor(cursor);
                 }
-                Log.d(TAG, "DefaultNewCallsQuery: " + newCalls.length + " new calls");
                 return newCalls;
             } finally {
                 MoreCloseables.closeQuietly(cursor);
diff --git a/src/com/android/contacts/detail/ContactDetailFragment.java b/src/com/android/contacts/detail/ContactDetailFragment.java
index f3f8e8a..767d366 100644
--- a/src/com/android/contacts/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/detail/ContactDetailFragment.java
@@ -39,6 +39,7 @@
 import com.android.contacts.util.Constants;
 import com.android.contacts.util.DataStatus;
 import com.android.contacts.util.DateUtils;
+import com.android.contacts.util.StructuredPostalUtils;
 import com.android.contacts.util.PhoneCapabilityTester;
 import com.android.contacts.widget.TransitionAnimationView;
 import com.android.internal.telephony.ITelephony;
@@ -634,8 +635,7 @@
                 } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     // Build postal entries
                     entry.maxLines = POSTAL_ADDRESS_MAX_LINES;
-                    entry.intent = new Intent(
-                            Intent.ACTION_VIEW, Uri.parse("geo:0,0?q=" + Uri.encode(entry.data)));
+                    entry.intent = StructuredPostalUtils.getViewPostalAddressIntent(entry.data);
                     mPostalEntries.add(entry);
                 } else if (Im.CONTENT_ITEM_TYPE.equals(mimeType) && hasData) {
                     // Build IM entries
diff --git a/src/com/android/contacts/detail/ContactLoaderFragment.java b/src/com/android/contacts/detail/ContactLoaderFragment.java
index 9417ee7..692c2ea 100644
--- a/src/com/android/contacts/detail/ContactLoaderFragment.java
+++ b/src/com/android/contacts/detail/ContactLoaderFragment.java
@@ -191,7 +191,7 @@
                 // This shouldn't ever happen, so throw an exception. The {@link ContactLoader}
                 // should log the actual exception.
                 throw new IllegalStateException("Failed to load contact", data.getException());
-            } else if (data == ContactLoader.Result.NOT_FOUND) {
+            } else if (data.isNotFound()) {
                 Log.i(TAG, "No contact found: " + ((ContactLoader)loader).getLookupUri());
                 mContactData = null;
             } else {
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index d1c5868..1790b9e 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -132,7 +132,6 @@
 
     private boolean mShowOptionsMenu;
 
-    private boolean mHasVoicemail = false;
 
     // Last number dialed, retrieved asynchronously from the call DB
     // in onCreate. This number is displayed when the user hits the
@@ -271,8 +270,6 @@
 
         mAdditionalButtonsRow = fragmentView.findViewById(R.id.dialpadAdditionalButtons);
 
-        initVoicemailButton();
-
         mSearchButton = mAdditionalButtonsRow.findViewById(R.id.searchButton);
         if (mSearchButton != null) {
             mSearchButton.setOnClickListener(this);
@@ -790,8 +787,15 @@
                 return true;
             }
             case R.id.one: {
-                if (mHasVoicemail && isDigitsEmpty()) {
-                    callVoicemail();
+                if (isDigitsEmpty()) {
+                    if (isVoicemailAvailable()) {
+                        callVoicemail();
+                    } else if (getActivity() != null) {
+                        DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
+                                R.string.dialog_voicemail_not_ready_title,
+                                R.string.dialog_voicemail_not_ready_message);
+                        dialogFragment.show(getFragmentManager(), "voicemail_not_ready");
+                    }
                     return true;
                 }
                 return false;
@@ -810,23 +814,57 @@
         getActivity().finish();
     }
 
-    public static class CallProhibitedDialogFragment extends DialogFragment {
-        public static CallProhibitedDialogFragment newInstance() {
-            return new CallProhibitedDialogFragment();
+    public static class ErrorDialogFragment extends DialogFragment {
+        private int mTitleResId;
+        private Integer mMessageResId;  // can be null
+
+        private static final String ARG_TITLE_RES_ID = "argTitleResId";
+        private static final String ARG_MESSAGE_RES_ID = "argMessageResId";
+
+        public static ErrorDialogFragment newInstance(int titleResId) {
+            return newInstanceInter(titleResId, null);
+        }
+
+        public static ErrorDialogFragment newInstance(int titleResId, int messageResId) {
+            return newInstanceInter(titleResId, messageResId);
+        }
+
+        private static ErrorDialogFragment newInstanceInter(
+                int titleResId, Integer messageResId) {
+            final ErrorDialogFragment fragment = new ErrorDialogFragment();
+            final Bundle args = new Bundle();
+            args.putInt(ARG_TITLE_RES_ID, titleResId);
+            if (messageResId != null) {
+                args.putInt(ARG_MESSAGE_RES_ID, messageResId);
+            }
+            fragment.setArguments(args);
+            return fragment;
+        }
+
+        @Override
+        public void onCreate(Bundle savedInstanceState) {
+            super.onCreate(savedInstanceState);
+            mTitleResId = getArguments().getInt(ARG_TITLE_RES_ID);
+            if (getArguments().containsKey(ARG_MESSAGE_RES_ID)) {
+                mMessageResId = getArguments().getInt(ARG_MESSAGE_RES_ID);
+            }
         }
 
         @Override
         public Dialog onCreateDialog(Bundle savedInstanceState) {
-            return new AlertDialog.Builder(getActivity())
-                    .setTitle(R.string.phone_call_prohibited)
+            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+            builder.setTitle(mTitleResId)
                     .setPositiveButton(android.R.string.ok,
                             new DialogInterface.OnClickListener() {
                                 @Override
                                 public void onClick(DialogInterface dialog, int which) {
                                     dismiss();
                                 }
-                            })
-                    .create();
+                            });
+            if (mMessageResId != null) {
+                builder.setMessage(mMessageResId);
+            }
+            return builder.create();
         }
     }
 
@@ -892,7 +930,8 @@
                     && (SystemProperties.getInt("persist.radio.otaspdial", 0) != 1)) {
                 Log.i(TAG, "The phone number is prohibited explicitly by a rule.");
                 if (getActivity() != null) {
-                    DialogFragment dialogFragment = CallProhibitedDialogFragment.newInstance();
+                    DialogFragment dialogFragment = ErrorDialogFragment.newInstance(
+                                    R.string.dialog_phone_call_prohibited_title);
                     dialogFragment.show(getFragmentManager(), "phone_prohibited_dialog");
                 }
 
@@ -1290,13 +1329,19 @@
 
     /**
      * Check if voicemail is enabled/accessible.
+     *
+     * @return true if voicemail is enabled and accessibly. Note that this can be false
+     * "temporarily" after the app boot.
+     * @see TelephonyManager#getVoiceMailNumber()
      */
-    private void initVoicemailButton() {
+    private boolean isVoicemailAvailable() {
         try {
-            mHasVoicemail = TelephonyManager.getDefault().getVoiceMailNumber() != null;
+            return (TelephonyManager.getDefault().getVoiceMailNumber() != null);
         } catch (SecurityException se) {
             // Possibly no READ_PHONE_STATE privilege.
+            Log.w(TAG, "SecurityException is thrown. Maybe privilege isn't sufficient.");
         }
+        return false;
     }
 
     /**
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index f7e0c23..844f892 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -1710,7 +1710,7 @@
         public void onLoadFinished(Loader<ContactLoader.Result> loader, ContactLoader.Result data) {
             final long loaderCurrentTime = SystemClock.elapsedRealtime();
             Log.v(TAG, "Time needed for loading: " + (loaderCurrentTime-mLoaderStartTime));
-            if (data == ContactLoader.Result.NOT_FOUND || data.isError()) {
+            if (!data.isLoaded()) {
                 // Item has been deleted
                 Log.i(TAG, "No contact found. Closing activity");
                 if (mListener != null) mListener.onContactNotFound();
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index 88b3843..84a34bd 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -21,6 +21,7 @@
 import com.android.contacts.model.AccountType.EditType;
 import com.android.contacts.model.DataKind;
 import com.android.contacts.util.Constants;
+import com.android.contacts.util.StructuredPostalUtils;
 import com.android.contacts.util.PhoneCapabilityTester;
 
 import android.content.ContentUris;
@@ -35,6 +36,7 @@
 import android.provider.ContactsContract.CommonDataKinds.Im;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.CommonDataKinds.SipAddress;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
 import android.provider.ContactsContract.CommonDataKinds.Website;
 import android.provider.ContactsContract.Data;
 import android.text.TextUtils;
@@ -202,6 +204,11 @@
                     }
                 }
             }
+        } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
+            final String postalAddress = getAsString(cursor, StructuredPostal.FORMATTED_ADDRESS);
+            if (!TextUtils.isEmpty(postalAddress)) {
+                mIntent = StructuredPostalUtils.getViewPostalAddressIntent(postalAddress);
+            }
         }
 
         if (mIntent == null) {
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index 9f7e18f..2ce15ff 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -133,7 +133,7 @@
         final RemoteViews views = new RemoteViews(context.getPackageName(),
                 R.layout.social_widget);
 
-        if (contactData.isError() || contactData == ContactLoader.Result.NOT_FOUND) {
+        if (!contactData.isLoaded()) {
             setDisplayNameAndSnippet(context, views,
                     context.getString(R.string.invalidContactMessage), null, null, null);
             setPhoto(views, ContactBadgeUtil.loadDefaultAvatarPhoto(context, false, false));
diff --git a/src/com/android/contacts/util/StructuredPostalUtils.java b/src/com/android/contacts/util/StructuredPostalUtils.java
new file mode 100644
index 0000000..59d38ea
--- /dev/null
+++ b/src/com/android/contacts/util/StructuredPostalUtils.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2011 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.util;
+
+import android.content.Intent;
+import android.net.Uri;
+
+public class StructuredPostalUtils {
+    private StructuredPostalUtils() {
+    }
+
+    public static Intent getViewPostalAddressIntent(String postalAddress) {
+        return new Intent(Intent.ACTION_VIEW, getPostalAddressUri(postalAddress));
+    }
+
+    public static Uri getPostalAddressUri(String postalAddress) {
+        return Uri.parse("geo:0,0?q=" + Uri.encode(postalAddress));
+    }
+}
diff --git a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
index becc133..9a84f7c 100644
--- a/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
+++ b/src/com/android/contacts/voicemail/VoicemailPlaybackPresenter.java
@@ -555,9 +555,11 @@
             public void run() {
                 int currentPosition = 0;
                 synchronized (mLock) {
-                    if (mScheduledFuture != null) {
-                        currentPosition = mPlayer.getCurrentPosition();
+                    if (mScheduledFuture == null) {
+                        // This task has been canceled. Just stop now.
+                        return;
                     }
+                    currentPosition = mPlayer.getCurrentPosition();
                 }
                 mView.setClipPosition(currentPosition, mDuration.get());
             }