Merge "Import translations. DO NOT MERGE" into jb-mr1.1-dev
diff --git a/res/layout-sw580dp/quickcontact_activity.xml b/res/layout-sw580dp/quickcontact_activity.xml
index a97d86c..129a6e5 100644
--- a/res/layout-sw580dp/quickcontact_activity.xml
+++ b/res/layout-sw580dp/quickcontact_activity.xml
@@ -42,6 +42,7 @@
             android:background="@color/quickcontact_tab_indicator" />
         <android.support.v4.view.ViewPager
             android:id="@+id/item_list_pager"
+            android:background="@color/quickcontact_list_background"
             android:layout_width="match_parent"
             android:layout_height="160dip" />
     </LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 4e5b215..1ac9b87 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -136,11 +136,11 @@
     <!-- Menu item that deletes the currently selected group [CHAR LIMIT=30] -->
     <string name="menu_deleteGroup">Delete</string>
 
-    <!-- Menu item (in the action bar) that creates a new contacts [CHAR LIMIT=12] -->
-    <string name="menu_new_contact_action_bar">New</string>
+    <!-- Menu item (in the action bar) that creates a new contact [CHAR LIMIT=30] -->
+    <string name="menu_new_contact_action_bar">Add Contact</string>
 
-    <!-- Menu item (in the action bar) that creates a new group [CHAR LIMIT=12] -->
-    <string name="menu_new_group_action_bar">New</string>
+    <!-- Menu item (in the action bar) that creates a new group [CHAR LIMIT=30] -->
+    <string name="menu_new_group_action_bar">Add Group</string>
 
     <!-- Title of the confirmation dialog for separating contacts into multiple instances [CHAR LIMIT=26] -->
     <string name="splitConfirmation_title">Separate contact?</string>
diff --git a/src/com/android/contacts/dialpad/DialpadFragment.java b/src/com/android/contacts/dialpad/DialpadFragment.java
index a310b88..855c134 100644
--- a/src/com/android/contacts/dialpad/DialpadFragment.java
+++ b/src/com/android/contacts/dialpad/DialpadFragment.java
@@ -271,14 +271,12 @@
 
         mDigitsContainer = fragmentView.findViewById(R.id.digits_container);
         mDigits = (EditText) fragmentView.findViewById(R.id.digits);
-        mDigits.setKeyListener(DialerKeyListener.getInstance());
+        mDigits.setKeyListener(UnicodeDialerKeyListener.INSTANCE);
         mDigits.setOnClickListener(this);
         mDigits.setOnKeyListener(this);
         mDigits.setOnLongClickListener(this);
         mDigits.addTextChangedListener(this);
-
         PhoneNumberFormatter.setPhoneNumberFormattingTextWatcher(getActivity(), mDigits);
-
         // Check for the presence of the keypad
         View oneButton = fragmentView.findViewById(R.id.one);
         if (oneButton != null) {
diff --git a/src/com/android/contacts/dialpad/UnicodeDialerKeyListener.java b/src/com/android/contacts/dialpad/UnicodeDialerKeyListener.java
new file mode 100644
index 0000000..faddcea
--- /dev/null
+++ b/src/com/android/contacts/dialpad/UnicodeDialerKeyListener.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2012 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.dialpad;
+
+import android.telephony.PhoneNumberUtils;
+import android.text.Spanned;
+import android.text.method.DialerKeyListener;
+
+/**
+ * {@link DialerKeyListener} with Unicode support. Converts any Unicode(e.g. Arabic) characters
+ * that represent digits into digits before filtering the results so that we can support
+ * pasted digits from Unicode languages.
+ */
+public class UnicodeDialerKeyListener extends DialerKeyListener {
+    public static final UnicodeDialerKeyListener INSTANCE = new UnicodeDialerKeyListener();
+
+    @Override
+    public CharSequence filter(CharSequence source, int start, int end,
+            Spanned dest, int dstart, int dend) {
+        CharSequence converted = PhoneNumberUtils.replaceUnicodeDigits(source.toString());
+        // PhoneNumberUtils.replaceUnicodeDigits performs a character for character replacement,
+        // so we can assume that start and end positions should remain unchanged.
+        CharSequence result = super.filter(converted, start, end, dest, dstart, dend);
+        if (result == null) {
+            if (source.equals(converted)) {
+                // There was no conversion or filtering performed. Just return null according to
+                // the behavior of DialerKeyListener.
+                return null;
+            } else {
+                // filter returns null if the charsequence is to be returned unchanged/unfiltered.
+                // But in this case we do want to return a modified character string (even if
+                // none of the characters in the modified string are filtered). So if
+                // result == null we return the unfiltered but converted numeric string instead.
+                return converted.subSequence(start, end);
+            }
+        }
+        return result;
+    }
+}
diff --git a/src/com/android/contacts/editor/ContactEditorFragment.java b/src/com/android/contacts/editor/ContactEditorFragment.java
index e29e488..028c01f 100644
--- a/src/com/android/contacts/editor/ContactEditorFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorFragment.java
@@ -118,9 +118,6 @@
     private static final String KEY_IS_USER_PROFILE = "isUserProfile";
     private static final String KEY_UPDATED_PHOTOS = "updatedPhotos";
 
-    private static final String[] VALID_ACTIONS = {Intent.ACTION_EDIT, Intent.ACTION_INSERT,
-            ContactEditorActivity.ACTION_SAVE_COMPLETED};
-
     public static final String SAVE_MODE_EXTRA_KEY = "saveMode";
 
 
@@ -405,13 +402,13 @@
      * @throws IllegalArgumentException when the action is invalid.
      */
     private void validateAction(String action) {
-        for (String validAction : VALID_ACTIONS) {
-            if (validAction.equals(action)) {
-                return;
-            }
+        if (Intent.ACTION_EDIT.equals(action) || Intent.ACTION_INSERT.equals(action) ||
+                ContactEditorActivity.ACTION_SAVE_COMPLETED.equals(action)) {
+            return;
         }
         throw new IllegalArgumentException("Unknown Action String " + mAction +
-                ". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT);
+                ". Only support " + Intent.ACTION_EDIT + " or " + Intent.ACTION_INSERT + " or " +
+                ContactEditorActivity.ACTION_SAVE_COMPLETED);
     }
 
     @Override
diff --git a/src/com/android/contacts/group/GroupDetailFragment.java b/src/com/android/contacts/group/GroupDetailFragment.java
index 6294b40..e0dce8c 100644
--- a/src/com/android/contacts/group/GroupDetailFragment.java
+++ b/src/com/android/contacts/group/GroupDetailFragment.java
@@ -155,6 +155,7 @@
                 R.id.group_source_view_container);
         mEmptyView = mRootView.findViewById(android.R.id.empty);
         mMemberListView = (ListView) mRootView.findViewById(android.R.id.list);
+        mMemberListView.setItemsCanFocus(true);
         mMemberListView.setAdapter(mAdapter);
 
         return mRootView;
diff --git a/src/com/android/contacts/model/dataitem/DataItem.java b/src/com/android/contacts/model/dataitem/DataItem.java
index 25c44cb..391c657 100644
--- a/src/com/android/contacts/model/dataitem/DataItem.java
+++ b/src/com/android/contacts/model/dataitem/DataItem.java
@@ -206,6 +206,17 @@
         return actionBody == null ? null : actionBody.toString();
     }
 
+    /**
+     * This builds the data string(intended for display) depending on the type of data item. It
+     * returns the same value as {@link #buildDataString} by default, but certain data items can
+     * override it to provide their version of formatted data strings.
+     *
+     * @return Data string representing the data item, possibly formatted for display
+     */
+    public String buildDataStringForDisplay() {
+        return buildDataString();
+    }
+
     public String getKindString() {
         final DataKind kind = getDataKind();
         return (kind.titleRes == -1 || kind.titleRes == 0) ? ""
diff --git a/src/com/android/contacts/model/dataitem/PhoneDataItem.java b/src/com/android/contacts/model/dataitem/PhoneDataItem.java
index 61d62c1..3931563 100644
--- a/src/com/android/contacts/model/dataitem/PhoneDataItem.java
+++ b/src/com/android/contacts/model/dataitem/PhoneDataItem.java
@@ -70,4 +70,17 @@
         }
     }
 
+    /**
+     * Returns the formatted phone number (if already computed using {@link
+     * #computeFormattedPhoneNumber}). Otherwise this method returns the unformatted phone number.
+     */
+    @Override
+    public String buildDataStringForDisplay() {
+        final String formatted = getFormattedPhoneNumber();
+        if (formatted != null) {
+            return formatted;
+        } else {
+            return getNumber();
+        }
+    }
 }
diff --git a/src/com/android/contacts/quickcontact/DataAction.java b/src/com/android/contacts/quickcontact/DataAction.java
index c10c338..829475f 100644
--- a/src/com/android/contacts/quickcontact/DataAction.java
+++ b/src/com/android/contacts/quickcontact/DataAction.java
@@ -95,7 +95,7 @@
         }
 
         mIsPrimary = item.isSuperPrimary();
-        mBody = item.buildDataString();
+        mBody = item.buildDataStringForDisplay();
 
         mDataId = item.getId();
         mDataUri = ContentUris.withAppendedId(Data.CONTENT_URI, mDataId);
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 16814dc..d39b380 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -546,7 +546,10 @@
             if (mLookupUri == null) {
                 Log.wtf(TAG, "Lookup uri wasn't initialized. Loader was started too early");
             }
-            return new ContactLoader(getApplicationContext(), mLookupUri, false);
+            return new ContactLoader(getApplicationContext(), mLookupUri,
+                    false /*loadGroupMetaData*/, false /*loadStreamItems*/,
+                    false /*loadInvitableAccountTypes*/, false /*postViewNotification*/,
+                    true /*computeFormattedPhoneNumber*/);
         }
     };
 
@@ -587,13 +590,18 @@
             final CheckableImageView actionView = getActionViewAt(position);
             mTrackScroller.requestChildRectangleOnScreen(actionView,
                     new Rect(0, 0, actionView.getWidth(), actionView.getHeight()), false);
+            renderSelectedRectangle(position, 0);
         }
 
         @Override
         public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+            renderSelectedRectangle(position, positionOffset);
+        }
+
+        private void renderSelectedRectangle(int position, float positionOffset) {
             final RelativeLayout.LayoutParams layoutParams =
                     (RelativeLayout.LayoutParams) mSelectedTabRectangle.getLayoutParams();
-            final int width = mSelectedTabRectangle.getWidth();
+            final int width = layoutParams.width;
             layoutParams.leftMargin = (int) ((position + positionOffset) * width);
             mSelectedTabRectangle.setLayoutParams(layoutParams);
         }
diff --git a/tests/src/com/android/contacts/dialpad/UnicodeDialerKeyListenerTest.java b/tests/src/com/android/contacts/dialpad/UnicodeDialerKeyListenerTest.java
new file mode 100644
index 0000000..3dbe18c
--- /dev/null
+++ b/tests/src/com/android/contacts/dialpad/UnicodeDialerKeyListenerTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2012 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.dialpad;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.contacts.dialpad.UnicodeDialerKeyListener;
+
+import junit.framework.TestCase;
+/**
+ * Test case for {@link UnicodeDialerKeyListener}.
+ *
+ * adb shell am instrument -w -e class com.android.contacts.dialpad.UnicodeDialerKeyListenerTest \
+       com.android.contacts.tests/android.test.InstrumentationTestRunner
+ */
+@SmallTest
+public class UnicodeDialerKeyListenerTest extends TestCase {
+    private static UnicodeDialerKeyListener mUnicodeDialerKeyListener;
+
+    // Pasted numeric digits should remain unchanged
+    public void testNumericDigits() {
+        // The last 3 arguments don't matter because {@link NumberKeyListener} doesn't care
+        // about dest, dstart, dend in
+        // public CharSequence filter (CharSequence source, int start, int end,
+        //         Spanned dest, int dstart, int dend)
+        // anyway. This applies to all tests.
+        assertEquals(null, mUnicodeDialerKeyListener.filter("111222333", 0, 9, null, 0, 0));
+    }
+
+    // Pasted Arabic digits should be converted to ascii digits
+    public void testArabicDigits() {
+        assertEquals("0123456789", mUnicodeDialerKeyListener.filter("٠١٢٣٤٥٦٧٨٩", 0, 10,
+                null, 0, 0));
+    }
+
+    // Pasted Farsi(Persian) digits should be converted to ascii digits
+    // Note the difference in digits 4, 5 and 6 when compared to arabic. The rest of the digits
+    // look the same compared to the Arabic digits but they actually have different unicode codes.
+    public void testFarsiDigits() {
+        assertEquals("0123456789", mUnicodeDialerKeyListener.filter("۰۱۲۳۴۵۶۷۸۹", 0, 10,
+                null, 0, 0));
+    }
+
+    // This is a rare use case but we should make sure it works all the same.
+    public void testCombinationDigits() {
+        assertEquals("15102849177", mUnicodeDialerKeyListener.filter("۱510٢٨٤۹۱۷۷", 0, 11,
+                null, 0, 0));
+    }
+
+    // Test that a normal digit string with dashes is returned unfiltered
+    public void testDashes() {
+        assertEquals(null, mUnicodeDialerKeyListener.filter("1510-284-9177", 0, 13,
+                null, 0, 0));
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        mUnicodeDialerKeyListener = UnicodeDialerKeyListener.INSTANCE;
+    }
+}