Merge "Fix contentDescription in dialpad."
diff --git a/res/layout/carousel_about_tab.xml b/res/layout/carousel_about_tab.xml
index bf67ee2..7f70eab 100644
--- a/res/layout/carousel_about_tab.xml
+++ b/res/layout/carousel_about_tab.xml
@@ -16,8 +16,9 @@
 
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/detail_tab_carousel_tab_width"
-    android:layout_height="@dimen/detail_tab_carousel_height"
+    android:layout_width="0dip"
+    android:layout_height="match_parent"
+    android:layout_weight="1"
     android:background="@color/detail_tab_background">
 
     <ImageView android:id="@+id/photo"
diff --git a/res/layout/carousel_updates_tab.xml b/res/layout/carousel_updates_tab.xml
index 9da2272..d235280 100644
--- a/res/layout/carousel_updates_tab.xml
+++ b/res/layout/carousel_updates_tab.xml
@@ -16,8 +16,9 @@
 
 <RelativeLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="@dimen/detail_tab_carousel_tab_width"
-    android:layout_height="@dimen/detail_tab_carousel_height"
+    android:layout_width="0dip"
+    android:layout_height="match_parent"
+    android:layout_weight="1"
     android:background="@color/detail_tab_background">
 
     <!-- Transparent view to overlay on the contact's photo
diff --git a/res/layout/contact_detail_container_with_updates.xml b/res/layout/contact_detail_container_with_updates.xml
index de7d145..48f1800 100644
--- a/res/layout/contact_detail_container_with_updates.xml
+++ b/res/layout/contact_detail_container_with_updates.xml
@@ -26,8 +26,9 @@
         android:layout_width="match_parent"
         android:layout_height="match_parent" />
 
-    <com.android.contacts.detail.ContactDetailTabCarousel
+    <include
         android:id="@+id/tab_carousel"
+        layout="@layout/contact_detail_tab_carousel"
         android:layout_alignParentTop="true"
         android:layout_alignParentLeft="true"
         android:layout_width="match_parent"
diff --git a/res/layout/contact_detail_tab_carousel.xml b/res/layout/contact_detail_tab_carousel.xml
index a7321ee..175194c 100644
--- a/res/layout/contact_detail_tab_carousel.xml
+++ b/res/layout/contact_detail_tab_carousel.xml
@@ -14,16 +14,18 @@
      limitations under the License.
 -->
 
-<HorizontalScrollView
+<view
     xmlns:android="http://schemas.android.com/apk/res/android"
+    class="com.android.contacts.detail.ContactDetailTabCarousel"
     android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:scrollbars="none">
+    android:layout_height="@dimen/detail_tab_carousel_height"
+    android:scrollbars="none"
+    android:fadingEdge="none">
 
     <LinearLayout
         android:id="@+id/tab_container"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/detail_tab_carousel_height"
         android:orientation="horizontal">
 
         <include
@@ -36,4 +38,4 @@
 
     </LinearLayout>
 
-</HorizontalScrollView>
\ No newline at end of file
+</view>
\ No newline at end of file
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index b0624cb..a0c4845 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -91,9 +91,6 @@
     <!-- Height of the tab carousel on the contact detail page -->
     <dimen name="detail_tab_carousel_height">150dip</dimen>
 
-    <!-- Width of a tab in the tab carousel on the contact detail page -->
-    <dimen name="detail_tab_carousel_tab_width">240dip</dimen>
-
     <!-- Height of the tab text label in the tab carousel on the contact detail page -->
     <dimen name="detail_tab_carousel_tab_label_height">40dip</dimen>
 
diff --git a/src/com/android/contacts/ContactOptionsActivity.java b/src/com/android/contacts/ContactOptionsActivity.java
index 2c6142a..9ae8fe9 100644
--- a/src/com/android/contacts/ContactOptionsActivity.java
+++ b/src/com/android/contacts/ContactOptionsActivity.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts;
 
+import android.app.ActionBar;
 import android.app.Activity;
 import android.content.ContentValues;
 import android.content.Intent;
@@ -26,6 +27,7 @@
 import android.os.Bundle;
 import android.provider.ContactsContract.Contacts;
 import android.util.Log;
+import android.view.MenuItem;
 import android.view.View;
 import android.widget.CheckBox;
 import android.widget.TextView;
@@ -75,6 +77,11 @@
         label = (TextView)sendToVoicemailLayout.findViewById(R.id.label);
         label.setText(getString(R.string.actionIncomingCall));
 
+        ActionBar actionBar =  getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
         mSendToVoicemailCheckbox = (CheckBox)sendToVoicemailLayout.findViewById(R.id.checkbox);
     }
 
@@ -208,6 +215,19 @@
             ContactsSearchManager.startSearch(this, initialQuery);
         }
     }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                finish();
+                return true;
+            default:
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
 }
 
 
diff --git a/src/com/android/contacts/activities/ContactDetailActivity.java b/src/com/android/contacts/activities/ContactDetailActivity.java
index 9c403b3..b45ba39 100644
--- a/src/com/android/contacts/activities/ContactDetailActivity.java
+++ b/src/com/android/contacts/activities/ContactDetailActivity.java
@@ -33,7 +33,6 @@
 
 import android.accounts.Account;
 import android.app.ActionBar;
-import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
@@ -49,12 +48,12 @@
 import android.support.v4.view.ViewPager.OnPageChangeListener;
 import android.util.Log;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
-import android.view.View.OnClickListener;
-import android.view.LayoutInflater;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.ViewGroup;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
@@ -141,6 +140,11 @@
             }
         }
 
+        ActionBar actionBar =  getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
+
         Log.i(TAG, getIntent().getData().toString());
     }
 
@@ -466,10 +470,10 @@
             if (mTabCarousel == null) {
                 return;
             }
-            // Only re-position the tab carousel vertically if the FIRST item is still visible on
-            // the screen, otherwise the carousel should be in the correct place (pinned at the
-            // top).
+            // If the FIRST item is not visible on the screen, then the carousel must be pinned
+            // at the top of the screen.
             if (firstVisibleItem != 0) {
+                mTabCarousel.setY(-mTabCarousel.getAllowedVerticalScrollLength());
                 return;
             }
             View topView = view.getChildAt(firstVisibleItem);
@@ -477,7 +481,7 @@
                 return;
             }
             int amtToScroll = Math.max((int) view.getChildAt(firstVisibleItem).getY(),
-                    -mTabCarousel.getAllowedVerticalScrollLength());
+                    - mTabCarousel.getAllowedVerticalScrollLength());
             mTabCarousel.setY(amtToScroll);
         }
 
@@ -498,4 +502,17 @@
          */
         public boolean handleKeyDown(int keyCode);
     }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                finish();
+                return true;
+            default:
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
 }
diff --git a/src/com/android/contacts/activities/GroupDetailActivity.java b/src/com/android/contacts/activities/GroupDetailActivity.java
index 5d50efc..21900c6 100644
--- a/src/com/android/contacts/activities/GroupDetailActivity.java
+++ b/src/com/android/contacts/activities/GroupDetailActivity.java
@@ -20,9 +20,11 @@
 import com.android.contacts.R;
 import com.android.contacts.group.GroupDetailFragment;
 
+import android.app.ActionBar;
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
+import android.view.MenuItem;
 
 public class GroupDetailActivity extends ContactsActivity {
 
@@ -37,12 +39,15 @@
 
         setContentView(R.layout.group_detail_activity);
 
-        getActionBar().setDisplayHomeAsUpEnabled(true);
-
         GroupDetailFragment fragment = (GroupDetailFragment) getFragmentManager().findFragmentById(
                 R.id.group_detail_fragment);
         fragment.setListener(mFragmentListener);
         fragment.loadGroup(getIntent().getData());
+
+        ActionBar actionBar =  getActionBar();
+        if (actionBar != null) {
+            actionBar.setDisplayHomeAsUpEnabled(true);
+        }
     }
 
     private final GroupDetailFragment.Listener mFragmentListener =
@@ -66,4 +71,17 @@
             startActivity(intent);
         }
     };
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+
+        switch (item.getItemId()) {
+            case android.R.id.home:
+                finish();
+                return true;
+            default:
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
 }
diff --git a/src/com/android/contacts/detail/ContactDetailTabCarousel.java b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
index 2ce26c6..6e1b199 100644
--- a/src/com/android/contacts/detail/ContactDetailTabCarousel.java
+++ b/src/com/android/contacts/detail/ContactDetailTabCarousel.java
@@ -20,8 +20,8 @@
 import com.android.contacts.R;
 
 import android.content.Context;
+import android.content.res.Resources;
 import android.util.AttributeSet;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnTouchListener;
@@ -32,18 +32,27 @@
 /**
  * This is a horizontally scrolling carousel with 2 tabs: one to see info about the contact and
  * one to see updates from the contact.
- * TODO: Create custom views for the tabs so their width can be programatically set as 2/3 of the
- * screen width.
  */
 public class ContactDetailTabCarousel extends HorizontalScrollView implements OnTouchListener {
-    private static final String TAG = "ContactDetailTabCarousel";
+
+    private static final String TAG = ContactDetailTabCarousel.class.getSimpleName();
+
+    private static final int TAB_INDEX_ABOUT = 0;
+    private static final int TAB_INDEX_UPDATES = 1;
+    private static final int TAB_COUNT = 2;
+
+    private static final double TAB_WIDTH_SCREEN_PERCENTAGE = 0.75;
 
     private ImageView mPhotoView;
     private TextView mStatusView;
 
     private Listener mListener;
 
-    private View[] mTabs = new View[2];
+    private final View[] mTabs = new View[TAB_COUNT];
+
+    private int mTabWidth;
+    private int mTabHeight;
+    private int mTabDisplayLabelHeight;
 
     private int mAllowedHorizontalScrollLength = Integer.MIN_VALUE;
     private int mAllowedVerticalScrollLength = Integer.MIN_VALUE;
@@ -58,22 +67,78 @@
         public void onTabSelected(int position);
     }
 
-    public ContactDetailTabCarousel(Context context) {
-        this(context, null);
-    }
-
     public ContactDetailTabCarousel(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public ContactDetailTabCarousel(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-
-        final LayoutInflater inflater =
-            (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        inflater.inflate(R.layout.contact_detail_tab_carousel, this);
+        super(context, attrs);
 
         setOnTouchListener(this);
+
+        Resources resources = mContext.getResources();
+        mTabHeight = resources.getDimensionPixelSize(R.dimen.detail_tab_carousel_height);
+        mTabDisplayLabelHeight = resources.getDimensionPixelSize(
+                R.dimen.detail_tab_carousel_tab_label_height);
+        mAllowedVerticalScrollLength = mTabHeight - mTabDisplayLabelHeight;
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        View aboutView = findViewById(R.id.tab_about);
+        View updateView = findViewById(R.id.tab_update);
+
+        TextView aboutTab = (TextView) aboutView.findViewById(R.id.label);
+        aboutTab.setText(mContext.getString(R.string.contactDetailAbout));
+        aboutTab.setClickable(true);
+        aboutTab.setSelected(true);
+
+        TextView updatesTab = (TextView) updateView.findViewById(R.id.label);
+        updatesTab.setText(mContext.getString(R.string.contactDetailUpdates));
+        updatesTab.setClickable(true);
+
+        aboutTab.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mListener.onTabSelected(TAB_INDEX_ABOUT);
+            }
+        });
+        updatesTab.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mListener.onTabSelected(TAB_INDEX_ABOUT);
+            }
+        });
+
+        mTabs[TAB_INDEX_ABOUT] = aboutTab;
+        mTabs[TAB_INDEX_UPDATES] = updatesTab;
+
+        // Retrieve the photo view for the "about" tab
+        mPhotoView = (ImageView) aboutView.findViewById(R.id.photo);
+
+        // Retrieve the social update views for the "updates" tab
+        mStatusView = (TextView) updateView.findViewById(R.id.status);
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        measureChildren(widthMeasureSpec);
+    }
+
+    private void measureChildren(int widthMeasureSpec) {
+        int screenWidth = MeasureSpec.getSize(widthMeasureSpec);
+        // Compute the width of a tab as a fraction of the screen width
+        mTabWidth = (int) (TAB_WIDTH_SCREEN_PERCENTAGE * screenWidth);
+
+        // Find the allowed scrolling length by subtracting the current visible screen width
+        // from the total length of the tabs.
+        mAllowedHorizontalScrollLength = mTabWidth * TAB_COUNT - screenWidth;
+
+        // Set the child {@link LinearLayout} to be TAB_COUNT * the computed tab width so that the
+        // {@link LinearLayout}'s children (which are the tabs) will evenly split that width.
+        if (getChildCount() > 0) {
+            View child = getChildAt(0);
+            child.measure(MeasureSpec.makeMeasureSpec(TAB_COUNT * mTabWidth, MeasureSpec.EXACTLY),
+                    MeasureSpec.makeMeasureSpec(mTabHeight, MeasureSpec.EXACTLY));
+        }
     }
 
     @Override
@@ -86,19 +151,6 @@
      * Returns the number of pixels that this view can be scrolled horizontally.
      */
     public int getAllowedHorizontalScrollLength() {
-        // We can't compute this in the constructor because the view widths are 0, so do the
-        // calculation only when this getter method is called (all the views should be created
-        // by this time).
-        if (mAllowedHorizontalScrollLength == Integer.MIN_VALUE) {
-            // Find the total length of two tabs side-by-side
-            int totalLength = 0;
-            for (int i=0; i < mTabs.length; i++) {
-                totalLength += mTabs[i].getWidth();
-            }
-            // Find the allowed scrolling length by subtracting the current visible screen width
-            // from the total length of the tabs.
-            mAllowedHorizontalScrollLength = totalLength - getWidth();
-        }
         return mAllowedHorizontalScrollLength;
     }
 
@@ -107,16 +159,6 @@
      * the tab labels to still show.
      */
     public int getAllowedVerticalScrollLength() {
-        if (mAllowedVerticalScrollLength == Integer.MIN_VALUE) {
-            // Find the total height of a tab
-            View aboutView = findViewById(R.id.tab_about);
-            int totalHeight = aboutView.getHeight();
-            // Find the height of a tab label
-            TextView aboutTab = (TextView) aboutView.findViewById(R.id.label);
-            int labelHeight = aboutTab.getHeight();
-            // Find the allowed scrolling length by subtracting the two values
-            mAllowedVerticalScrollLength = totalHeight - labelHeight;
-        }
         return mAllowedVerticalScrollLength;
     }
 
@@ -153,40 +195,6 @@
             return;
         }
 
-        View aboutView = findViewById(R.id.tab_about);
-        View updateView = findViewById(R.id.tab_update);
-
-        TextView aboutTab = (TextView) aboutView.findViewById(R.id.label);
-        aboutTab.setText(mContext.getString(R.string.contactDetailAbout));
-        aboutTab.setClickable(true);
-        aboutTab.setSelected(true);
-
-        TextView updatesTab = (TextView) updateView.findViewById(R.id.label);
-        updatesTab.setText(mContext.getString(R.string.contactDetailUpdates));
-        updatesTab.setClickable(true);
-
-        aboutTab.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mListener.onTabSelected(0);
-            }
-        });
-        updatesTab.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View v) {
-                mListener.onTabSelected(1);
-            }
-        });
-
-        mTabs[0] = aboutTab;
-        mTabs[1] = updatesTab;
-
-        // Retrieve the photo view for the "about" tab
-        mPhotoView = (ImageView) aboutView.findViewById(R.id.photo);
-
-        // Retrieve the social update view for the "updates" tab
-        mStatusView = (TextView) updateView.findViewById(R.id.status);
-
         ContactDetailDisplayUtils.setPhoto(mContext, contactData, mPhotoView);
         ContactDetailDisplayUtils.setSocialSnippet(mContext, contactData, mStatusView);
     }
@@ -203,10 +211,10 @@
         switch (event.getAction()) {
             case MotionEvent.ACTION_DOWN:
                 mListener.onTouchDown();
-                return true;
+                return false;
             case MotionEvent.ACTION_UP:
                 mListener.onTouchUp();
-                return true;
+                return false;
         }
         return super.onTouchEvent(event);
     }
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index 347d705..af706b8 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -24,6 +24,7 @@
 import com.android.internal.telephony.ITelephony;
 import com.android.phone.CallLogAsync;
 import com.android.phone.HapticFeedback;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
 
 import android.app.Activity;
 import android.app.Fragment;
@@ -37,6 +38,7 @@
 import android.media.AudioManager;
 import android.media.ToneGenerator;
 import android.net.Uri;
+import android.os.AsyncTask;
 import android.os.Bundle;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -146,7 +148,34 @@
 
     private String mCurrentCountryIso;
 
-    PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+    /**
+     * May be null for a moment and filled by AsyncTask. Must not be touched outside UI thread.
+     */
+    private PhoneNumberFormattingTextWatcher mTextWatcher;
+
+    /**
+     * Delays {@link PhoneNumberFormattingTextWatcher} creation as it may cause disk read operation.
+     */
+    private final AsyncTask<Void, Void, Void> mTextWatcherLoadAsyncTask =
+            new AsyncTask<Void, Void, Void>() {
+
+        private PhoneNumberFormattingTextWatcher mTemporaryWatcher;
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            mTemporaryWatcher = new PhoneNumberFormattingTextWatcher(mCurrentCountryIso);
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            // Should be in UI thread.
+            mTextWatcher = mTemporaryWatcher;
+            mDigits.addTextChangedListener(mTextWatcher);
+        }
+    };
+
+    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
             /**
              * Listen for phone state changes so that we can take down the
              * "dialpad chooser" if the phone becomes idle while the
@@ -228,7 +257,14 @@
         mDigits.setOnKeyListener(this);
         mDigits.addTextChangedListener(this);
 
-        maybeAddNumberFormatting();
+        if (mTextWatcher == null) {
+            if (mTextWatcherLoadAsyncTask.getStatus() == AsyncTask.Status.PENDING) {
+                // Start loading text watcher for phone number, which requires disk read.
+                mTextWatcherLoadAsyncTask.execute();
+            }
+        } else {
+            mDigits.addTextChangedListener(mTextWatcher);
+        }
 
         // Check for the presence of the keypad
         View oneButton = fragmentView.findViewById(R.id.one);
@@ -283,10 +319,6 @@
         return mDigits;
     }
 
-    private void maybeAddNumberFormatting() {
-        mDigits.addTextChangedListener(new PhoneNumberFormattingTextWatcher(mCurrentCountryIso));
-    }
-
     /**
      * Handles the intent that launched us.
      *