Import translations. DO NOT MERGE
am: bd3aa3cf47  -s ours

* commit 'bd3aa3cf472f84de2fb856dd7c2c48ffbde8402e':
  Import translations. DO NOT MERGE
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 5c7a124..bcf8dcc 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -93,7 +93,7 @@
     <string name="dialer_settings_label" msgid="4305043242594150479">"Sozlamalar"</string>
     <string name="menu_newContact" msgid="1209922412763274638">"Yangi kontakt"</string>
     <string name="menu_allContacts" msgid="6948308384034051670">"Barcha kontaktlar"</string>
-    <string name="callDetailTitle" msgid="5340227785196217938">"Qo‘ng‘iroq tafsilotlari"</string>
+    <string name="callDetailTitle" msgid="5340227785196217938">"Qo‘ng‘roq tafsilotlari"</string>
     <string name="toast_call_detail_error" msgid="6947041258280380832">"Tafsilotlar mavjud emas"</string>
     <string name="dialer_useDtmfDialpad" msgid="1707548397435075040">"Tovushli raqam tergich"</string>
     <string name="dialer_returnToInCallScreen" msgid="3719386377550913067">"Qo‘ng‘iroqqa qaytish"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index 895275c..da7d95a 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -43,8 +43,8 @@
     <string name="notification_missedCallTitle" msgid="2078223183024451723">"未接來電"</string>
     <string name="notification_missedCallsTitle" msgid="4948840634672742797">"未接來電"</string>
     <string name="notification_missedCallsMsg" msgid="158790423221579961">"<xliff:g id="NUM_MISSED_CALLS">%s</xliff:g> 個未接來電"</string>
-    <string name="notification_missedCall_call_back" msgid="1991552423738343203">"回電"</string>
-    <string name="notification_missedCall_message" msgid="3042123733754151052">"短訊"</string>
+    <string name="notification_missedCall_call_back" msgid="1991552423738343203">"回撥電話"</string>
+    <string name="notification_missedCall_message" msgid="3042123733754151052">"傳送短訊"</string>
     <plurals name="notification_voicemail_title" formatted="false" msgid="9088953961148324851">
       <item quantity="other"><xliff:g id="COUNT">%1$d</xliff:g> 個留言</item>
       <item quantity="one">留言</item>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7e87b2f..3c319af 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -453,6 +453,12 @@
          The date will be replaced by 'Today' for voicemails created on the current day. For example: Today at 2:49 PM -->
     <string name="voicemailCallLogDateTimeFormat"><xliff:g id="date" example="Jul 25, 2014">%1$s</xliff:g> at <xliff:g id="time" example="2:49 PM">%2$s</xliff:g></string>
 
+    <!-- Format for duration of voicemails which are displayed when viewing voicemail logs. For example "01:22" -->
+    <string name="voicemailDurationFormat"><xliff:g id="minutes" example="10">%1$02d</xliff:g>:<xliff:g id="seconds" example="20">%2$02d</xliff:g></string>
+
+    <!-- A format string used for displaying the date, time and duration for a voicemail call log. For example: Jul 25, 2014 at 2:49 PM • 00:34 -->
+    <string name="voicemailCallLogDateTimeFormatWithDuration"><xliff:g id="dateAndTime" example="Jul 25, 2014 at 2:49PM">%1$s</xliff:g> \u2022 <xliff:g id="duration" example="01:22">%2$s</xliff:g></string>
+
     <!-- Dialog message which is shown when the user tries to make a phone call
          to prohibited phone numbers [CHAR LIMIT=NONE] -->
     <string name="dialog_phone_call_prohibited_message" msgid="4313552620858880999">Can\'t call this number</string>
diff --git a/src/com/android/dialer/calllog/CallLogAdapter.java b/src/com/android/dialer/calllog/CallLogAdapter.java
index af77d86..aa97f7d 100644
--- a/src/com/android/dialer/calllog/CallLogAdapter.java
+++ b/src/com/android/dialer/calllog/CallLogAdapter.java
@@ -164,6 +164,9 @@
                 mCurrentlyExpandedPosition = RecyclerView.NO_POSITION;
                 mCurrentlyExpandedRowId = NO_EXPANDED_LIST_ITEM;
             } else {
+                if (viewHolder.callType == CallLog.Calls.MISSED_TYPE) {
+                    CallLogAsyncTaskUtil.markCallAsRead(mContext, viewHolder.callIds);
+                }
                 expandViewHolderActions(viewHolder);
             }
 
@@ -482,7 +485,8 @@
         details.features = getCallFeatures(c, count);
         details.geocode = c.getString(CallLogQuery.GEOCODED_LOCATION);
         details.transcription = c.getString(CallLogQuery.TRANSCRIPTION);
-        if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE) {
+        if (details.callTypes[0] == CallLog.Calls.VOICEMAIL_TYPE ||
+                details.callTypes[0] == CallLog.Calls.MISSED_TYPE) {
             details.isRead = c.getInt(CallLogQuery.IS_READ) == 1;
         }
 
diff --git a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
index bb7bdbd..d73159c 100644
--- a/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
+++ b/src/com/android/dialer/calllog/CallLogAsyncTaskUtil.java
@@ -29,6 +29,7 @@
 import android.util.Log;
 
 import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.util.PermissionsUtil;
 import com.android.dialer.DialtactsActivity;
 import com.android.dialer.PhoneCallDetails;
 import com.android.dialer.util.AppCompatConstants;
@@ -51,6 +52,7 @@
         DELETE_CALL,
         DELETE_BLOCKED_CALL,
         MARK_VOICEMAIL_READ,
+        MARK_CALL_READ,
         GET_CALL_DETAILS,
     }
 
@@ -379,6 +381,39 @@
         });
     }
 
+    public static void markCallAsRead(final Context context, final long[] callIds) {
+        if (!PermissionsUtil.hasPhonePermissions(context)) {
+            return;
+        }
+        if (sAsyncTaskExecutor == null) {
+            initTaskExecutor();
+        }
+
+        sAsyncTaskExecutor.submit(Tasks.MARK_CALL_READ, new AsyncTask<Void, Void, Void>() {
+            @Override
+            public Void doInBackground(Void... params) {
+
+                StringBuilder where = new StringBuilder();
+                where.append(CallLog.Calls.TYPE).append(" = ").append(CallLog.Calls.MISSED_TYPE);
+                where.append(" AND ");
+
+                Long[] callIdLongs = new Long[callIds.length];
+                for (int i = 0; i < callIds.length; i++) {
+                    callIdLongs[i] = callIds[i];
+                }
+                where.append(CallLog.Calls._ID).append(
+                        " IN (" + TextUtils.join(",", callIdLongs) + ")");
+
+                ContentValues values = new ContentValues(1);
+                values.put(CallLog.Calls.IS_READ, "1");
+                context.getContentResolver().update(
+                        CallLog.Calls.CONTENT_URI, values, where.toString(), null);
+                ((DialtactsActivity) context).updateTabUnreadCounts();
+                return null;
+            }
+        });
+    }
+
     @VisibleForTesting
     public static void resetForTest() {
         sAsyncTaskExecutor = null;
diff --git a/src/com/android/dialer/calllog/CallLogFragment.java b/src/com/android/dialer/calllog/CallLogFragment.java
index fa6deaf..006fec7 100644
--- a/src/com/android/dialer/calllog/CallLogFragment.java
+++ b/src/com/android/dialer/calllog/CallLogFragment.java
@@ -290,6 +290,9 @@
     public void onVoicemailUnreadCountFetched(Cursor cursor) {}
 
     @Override
+    public void onMissedCallsUnreadCountFetched(Cursor cursor) {}
+
+    @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) {
         View view = inflater.inflate(R.layout.call_log_fragment, container, false);
 
@@ -403,7 +406,6 @@
     @Override
     public void fetchCalls() {
         mCallLogQueryHandler.fetchCalls(mCallTypeFilter, mDateLimit);
-
         if (mVoicemailPlaybackPresenter != null) {
             ((ListsFragment) getParentFragment()).updateTabUnreadCounts();
         }
@@ -472,7 +474,7 @@
 
             fetchCalls();
             mCallLogQueryHandler.fetchVoicemailStatus();
-
+            mCallLogQueryHandler.fetchMissedCallsUnreadCount();
             updateOnTransition(true /* onEntry */);
             mRefreshDataRequired = false;
         } else {
@@ -496,12 +498,11 @@
             // On either of the transitions we update the missed call and voicemail notifications.
             // While exiting we additionally consume all missed calls (by marking them as read).
             mCallLogQueryHandler.markNewCallsAsOld();
-            if (!onEntry) {
-                mCallLogQueryHandler.markMissedCallsAsRead();
-            }
             if (mCallTypeFilter == Calls.VOICEMAIL_TYPE) {
                 CallLogNotificationsHelper.updateVoicemailNotifications(getActivity());
-            } else {
+            } else if (((ListsFragment) getParentFragment()).getCurrentTabIndex() ==
+                    ListsFragment.TAB_INDEX_HISTORY && !onEntry) {
+                mCallLogQueryHandler.markMissedCallsAsRead();
                 CallLogNotificationsHelper.removeMissedCallNotifications(getActivity());
             }
         }
diff --git a/src/com/android/dialer/calllog/CallLogQueryHandler.java b/src/com/android/dialer/calllog/CallLogQueryHandler.java
index 4cb835b..9ff74cf 100644
--- a/src/com/android/dialer/calllog/CallLogQueryHandler.java
+++ b/src/com/android/dialer/calllog/CallLogQueryHandler.java
@@ -62,6 +62,8 @@
     private static final int QUERY_VOICEMAIL_STATUS_TOKEN = 57;
     /** The token for the query to fetch the number of unread voicemails. */
     private static final int QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN = 58;
+    /** The token for the query to fetch the number of missed calls. */
+    private static final int QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN = 59;
 
     private final int mLogLimit;
 
@@ -225,19 +227,25 @@
         if (!PermissionsUtil.hasPhonePermissions(mContext)) {
             return;
         }
-        // Mark all "new" calls as not new anymore.
-        StringBuilder where = new StringBuilder();
-        where.append(Calls.IS_READ).append(" = 0");
-        where.append(" AND ");
-        where.append(Calls.TYPE).append(" = ").append(Calls.MISSED_TYPE);
 
         ContentValues values = new ContentValues(1);
         values.put(Calls.IS_READ, "1");
 
         startUpdate(UPDATE_MARK_MISSED_CALL_AS_READ_TOKEN, null, Calls.CONTENT_URI, values,
-                where.toString(), null);
+                getUnreadMissedCallsQuery(), null);
     }
 
+    /** Fetch all missed calls received since last time the tab was opened. */
+    public void fetchMissedCallsUnreadCount() {
+        if (!PermissionsUtil.hasPhonePermissions(mContext)) {
+            return;
+        }
+
+        startQuery(QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN, null, Calls.CONTENT_URI,
+                new String[]{Calls._ID}, getUnreadMissedCallsQuery(), null, null);
+    }
+
+
     @Override
     protected synchronized void onNotNullableQueryComplete(int token, Object cookie,
             Cursor cursor) {
@@ -253,6 +261,8 @@
                 updateVoicemailStatus(cursor);
             } else if (token == QUERY_VOICEMAIL_UNREAD_COUNT_TOKEN) {
                 updateVoicemailUnreadCount(cursor);
+            } else if (token == QUERY_MISSED_CALLS_UNREAD_COUNT_TOKEN) {
+                updateMissedCallsUnreadCount(cursor);
             } else {
                 Log.w(TAG, "Unknown query completed: ignoring: " + token);
             }
@@ -276,6 +286,17 @@
 
     }
 
+    /**
+     * @return Query string to get all unread missed calls.
+     */
+    private String getUnreadMissedCallsQuery() {
+        StringBuilder where = new StringBuilder();
+        where.append(Calls.IS_READ).append(" = 0");
+        where.append(" AND ");
+        where.append(Calls.TYPE).append(" = ").append(Calls.MISSED_TYPE);
+        return where.toString();
+    }
+
     private void updateVoicemailStatus(Cursor statusCursor) {
         final Listener listener = mListener.get();
         if (listener != null) {
@@ -290,6 +311,13 @@
         }
     }
 
+    private void updateMissedCallsUnreadCount(Cursor statusCursor) {
+        final Listener listener = mListener.get();
+        if (listener != null) {
+            listener.onMissedCallsUnreadCountFetched(statusCursor);
+        }
+    }
+
     /** Listener to completion of various queries. */
     public interface Listener {
         /** Called when {@link CallLogQueryHandler#fetchVoicemailStatus()} completes. */
@@ -298,6 +326,9 @@
         /** Called when {@link CallLogQueryHandler#fetchVoicemailUnreadCount()} completes. */
         void onVoicemailUnreadCountFetched(Cursor cursor);
 
+        /** Called when {@link CallLogQueryHandler#fetchMissedCallsUnreadCount()} completes. */
+        void onMissedCallsUnreadCountFetched(Cursor cursor);
+
         /**
          * Called when {@link CallLogQueryHandler#fetchCalls(int)} complete.
          * Returns true if takes ownership of cursor.
diff --git a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
index e6b8508..be02e4c 100644
--- a/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
+++ b/src/com/android/dialer/calllog/PhoneCallDetailsHelper.java
@@ -40,6 +40,7 @@
 
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Helper class to fill in the views in {@link PhoneCallDetailsViews}.
@@ -110,10 +111,8 @@
             callCount = null;
         }
 
-        CharSequence callLocationAndDate = getCallLocationAndDate(details);
-
-        // Set the call count, location and date.
-        setCallCountAndDate(views, callCount, callLocationAndDate);
+        // Set the call count, location, date and if voicemail, set the duration.
+        setDetailText(views, callCount, details);
 
         // Set the account label if it exists.
         String accountLabel = mCallLogCache.getAccountLabel(details.accountHandle);
@@ -314,10 +313,11 @@
         }
     }
 
-    /** Sets the call count and date. */
-    private void setCallCountAndDate(PhoneCallDetailsViews views, Integer callCount,
-            CharSequence dateText) {
+    /** Sets the call count, date, and if it is a voicemail, sets the duration. */
+    private void setDetailText(PhoneCallDetailsViews views, Integer callCount,
+                               PhoneCallDetails details) {
         // Combine the count (if present) and the date.
+        CharSequence dateText = getCallLocationAndDate(details);
         final CharSequence text;
         if (callCount != null) {
             text = mResources.getString(
@@ -326,6 +326,22 @@
             text = dateText;
         }
 
-        views.callLocationAndDate.setText(text);
+        if (details.callTypes[0] == Calls.VOICEMAIL_TYPE) {
+            views.callLocationAndDate.setText(mResources.getString(
+                    R.string.voicemailCallLogDateTimeFormatWithDuration, text,
+                    getVoicemailDuration(details)));
+        } else {
+            views.callLocationAndDate.setText(text);
+        }
+
+    }
+
+    private String getVoicemailDuration(PhoneCallDetails details) {
+        long minutes = TimeUnit.SECONDS.toMinutes(details.duration);
+        long seconds = details.duration - TimeUnit.MINUTES.toSeconds(minutes);
+        if (minutes > 99) {
+            minutes = 99;
+        }
+        return mResources.getString(R.string.voicemailDurationFormat, minutes, seconds);
     }
 }
diff --git a/src/com/android/dialer/list/ListsFragment.java b/src/com/android/dialer/list/ListsFragment.java
index 5294498..ad3a0d8 100644
--- a/src/com/android/dialer/list/ListsFragment.java
+++ b/src/com/android/dialer/list/ListsFragment.java
@@ -191,6 +191,7 @@
         mCallLogQueryHandler =
                 new CallLogQueryHandler(getActivity(), getActivity().getContentResolver(), this);
         mCallLogQueryHandler.fetchVoicemailStatus();
+        mCallLogQueryHandler.fetchMissedCallsUnreadCount();
         Trace.endSection();
     }
 
@@ -346,6 +347,23 @@
     }
 
     @Override
+    public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+        if (getActivity() == null || getActivity().isFinishing() || cursor == null) {
+            return;
+        }
+
+        int count = 0;
+        try {
+            count = cursor.getCount();
+        } finally {
+            cursor.close();
+        }
+
+        mViewPagerTabs.setUnreadCount(count, TAB_INDEX_HISTORY);
+        mViewPagerTabs.updateTab(TAB_INDEX_HISTORY);
+    }
+
+    @Override
     public boolean onCallsFetched(Cursor statusCursor) {
         // Return false; did not take ownership of cursor
         return false;
@@ -360,8 +378,11 @@
      * expands a voicemail in the call log.
      */
     public void updateTabUnreadCounts() {
-        if (mHasActiveVoicemailProvider && mCallLogQueryHandler != null) {
-            mCallLogQueryHandler.fetchVoicemailUnreadCount();
+        if (mCallLogQueryHandler != null) {
+            mCallLogQueryHandler.fetchMissedCallsUnreadCount();
+            if (mHasActiveVoicemailProvider) {
+                mCallLogQueryHandler.fetchVoicemailUnreadCount();
+            }
         }
     }
 
diff --git a/src/com/android/dialer/util/PhoneNumberUtil.java b/src/com/android/dialer/util/PhoneNumberUtil.java
index 2699803..33f9873 100644
--- a/src/com/android/dialer/util/PhoneNumberUtil.java
+++ b/src/com/android/dialer/util/PhoneNumberUtil.java
@@ -100,7 +100,7 @@
      * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
      */
     public static String getGeoDescription(Context context, String number) {
-        Log.v(TAG, "getGeoDescription('" + number + "')...");
+        Log.v(TAG, "getGeoDescription('" + pii(number) + "')...");
 
         if (TextUtils.isEmpty(number)) {
             return null;
@@ -114,13 +114,13 @@
         String countryIso = TelephonyManagerUtils.getCurrentCountryIso(context, locale);
         Phonenumber.PhoneNumber pn = null;
         try {
-            Log.v(TAG, "parsing '" + number
+            Log.v(TAG, "parsing '" + pii(number)
                     + "' for countryIso '" + countryIso + "'...");
             pn = util.parse(number, countryIso);
-            Log.v(TAG, "- parsed number: " + pn);
+            Log.v(TAG, "- parsed number: " + pii(pn));
         } catch (NumberParseException e) {
             Log.v(TAG, "getGeoDescription: NumberParseException for incoming number '" +
-                    number + "'");
+                    pii(number) + "'");
         }
 
         if (pn != null) {
@@ -131,4 +131,8 @@
 
         return null;
     }
+
+    private static String pii(Object pii) {
+        return com.android.incallui.Log.pii(pii);
+    }
 }
diff --git a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java b/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
index 3134b14..80a0368 100644
--- a/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
+++ b/src/com/android/dialer/voicemail/VisualVoicemailEnabledChecker.java
@@ -91,6 +91,11 @@
     }
 
     @Override
+    public void onMissedCallsUnreadCountFetched(Cursor cursor) {
+        // Do nothing
+    }
+
+    @Override
     public boolean onCallsFetched(Cursor combinedCursor) {
         // Do nothing
         return false;
diff --git a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java b/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
index b6202b9..4e00a46 100644
--- a/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
+++ b/tests/src/com/android/dialer/calllog/PhoneCallDetailsHelperTest.java
@@ -34,6 +34,8 @@
 
 import java.util.GregorianCalendar;
 import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Unit tests for {@link PhoneCallDetailsHelper}.
@@ -141,7 +143,27 @@
 
     public void testVoicemailLocationNotShownWithDate() {
         setVoicemailPhoneCallDetailsWithDate(TEST_DATE);
-        assertLocationAndDateExactEquals("Jun 3 at 1:00 PM");
+        assertLocationAndDateExactEquals("Jun 3 at 1:00 PM • 99:20");
+    }
+
+    public void testVoicemailDuration() {
+        setVoicemailPhoneCallDetailsWithDuration(100);
+        assertDurationExactEquals("01:40");
+    }
+
+    public void testVoicemailDuration_Capped() {
+        setVoicemailPhoneCallDetailsWithDuration(TEST_DURATION);
+        assertDurationExactEquals("99:20");
+    }
+
+    public void testVoicemailDuration_Zero() {
+        setVoicemailPhoneCallDetailsWithDuration(0);
+        assertDurationExactEquals("00:00");
+    }
+
+    public void testVoicemailDuration_EvenMinute() {
+        setVoicemailPhoneCallDetailsWithDuration(60);
+        assertDurationExactEquals("01:00");
     }
 
     /** Asserts that a char sequence is actually a Spanned corresponding to the expected HTML. */
@@ -346,6 +368,14 @@
         assertEquals(text, mViews.callLocationAndDate.getText());
     }
 
+    /** Asserts that the duration is exactly as included in the location and date text field. */
+    private void assertDurationExactEquals(String text) {
+        Matcher matcher = Pattern.compile("(.*) (\\u2022) (\\d{2}:\\d{2})").matcher(
+                mViews.callLocationAndDate.getText());
+        assertEquals(true, matcher.matches());
+        assertEquals(text, matcher.group(3));
+    }
+
     /** Asserts that the video icon is shown. */
     private void assertIsVideoCall(boolean isVideoCall) {
         assertEquals(isVideoCall, mViews.callTypeIcons.isVideoShown());
@@ -407,6 +437,14 @@
         mHelper.setPhoneCallDetails(mViews, details);
     }
 
+    /** Sets the voice mail details with default values and the given duration. */
+    private void setVoicemailPhoneCallDetailsWithDuration(long duration) {
+        PhoneCallDetails details = getPhoneCallDetails();
+        details.duration = duration;
+        details.callTypes = new int[] {Calls.VOICEMAIL_TYPE};
+        mHelper.setPhoneCallDetails(mViews, details);
+    }
+
     /** Sets the phone call details with default values and the given call types using icons. */
     private void setPhoneCallDetailsWithCallTypeIcons(int... callTypes) {
         PhoneCallDetails details = getPhoneCallDetails();