From cded3beaf28a703e1ef8f71bbc6836e6806c3736 Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Fri, 9 Jun 2017 14:16:05 +0000 Subject: Revert "Update AOSP Dialer source from internal google3 repository at cl/158012278. am: 91ce7d2a47" This reverts commit c67d658e7daa453fe9ad9fd1a37f81eaf2048c44. Reason for revert: This CL broke the sailfish-userdebug_javac-all target on master. Change-Id: I9b54333a654c00154ca84f4ece84bea4f07cc19b --- .../android/contacts/common/ClipboardUtils.java | 2 + .../contacts/common/ContactPhotoManager.java | 31 +-- .../contacts/common/ContactPhotoManagerImpl.java | 5 +- .../android/contacts/common/ContactStatusUtil.java | 2 + .../com/android/contacts/common/ContactsUtils.java | 185 ++++++++++++++- java/com/android/contacts/common/GeoUtil.java | 55 +++++ .../android/contacts/common/MoreContactUtils.java | 36 +++ .../contacts/common/compat/ContactsCompat.java | 16 ++ .../contacts/common/compat/PhoneAccountCompat.java | 55 ++++- .../common/compat/PhoneNumberUtilsCompat.java | 132 ++++++++++- .../common/compat/TelephonyManagerCompat.java | 71 +++++- .../compat/telecom/TelecomManagerCompat.java | 260 ++++++++++++++++++++- .../contacts/common/dialog/CallSubjectDialog.java | 12 +- .../contacts/common/format/FormatUtils.java | 181 ++++++++++++++ .../contacts/common/format/TextHighlighter.java | 55 +---- .../common/format/testing/SpannedTestUtils.java | 85 +++++++ .../common/lettertiles/LetterTileDrawable.java | 28 --- .../common/list/ContactEntryListAdapter.java | 9 +- .../common/list/ContactEntryListFragment.java | 1 + .../contacts/common/list/ContactListItemView.java | 216 +++++++++-------- .../contacts/common/list/ContactTileView.java | 19 +- .../common/list/PhoneNumberListAdapter.java | 56 +++-- .../common/list/PhoneNumberPickerFragment.java | 66 ++---- .../contacts/common/list/ViewPagerTabs.java | 29 ++- .../contacts/common/location/CountryDetector.java | 221 ++++++++++++++++++ .../common/location/UpdateCountryService.java | 104 +++++++++ .../contacts/common/model/ContactLoader.java | 1 - .../common/model/account/BaseAccountType.java | 4 +- .../common/res/drawable-hdpi/ic_ab_search.png | Bin 0 -> 1115 bytes .../res/drawable-hdpi/ic_arrow_back_24dp.png | Bin 0 -> 612 bytes .../res/drawable-hdpi/ic_business_white_120dp.png | Bin 0 -> 2477 bytes .../common/res/drawable-hdpi/ic_call_24dp.png | Bin 0 -> 340 bytes .../common/res/drawable-hdpi/ic_close_dk.png | Bin 0 -> 609 bytes .../common/res/drawable-hdpi/ic_create_24dp.png | Bin 0 -> 370 bytes .../res/drawable-hdpi/ic_group_white_24dp.png | Bin 0 -> 389 bytes .../ic_history_white_drawable_24dp.png | Bin 0 -> 525 bytes .../res/drawable-hdpi/ic_info_outline_24dp.png | Bin 0 -> 485 bytes .../common/res/drawable-hdpi/ic_menu_back.png | Bin 0 -> 799 bytes .../common/res/drawable-hdpi/ic_menu_group_dk.png | Bin 0 -> 1954 bytes .../common/res/drawable-hdpi/ic_menu_group_lt.png | Bin 0 -> 1922 bytes .../res/drawable-hdpi/ic_menu_overflow_lt.png | Bin 0 -> 220 bytes .../common/res/drawable-hdpi/ic_menu_person_dk.png | Bin 0 -> 1439 bytes .../common/res/drawable-hdpi/ic_menu_person_lt.png | Bin 0 -> 1416 bytes .../ic_menu_remove_field_holo_light.png | Bin 0 -> 515 bytes .../common/res/drawable-hdpi/ic_menu_star_dk.png | Bin 0 -> 1438 bytes .../res/drawable-hdpi/ic_menu_star_holo_light.png | Bin 0 -> 1211 bytes .../common/res/drawable-hdpi/ic_menu_star_lt.png | Bin 0 -> 1414 bytes .../common/res/drawable-hdpi/ic_person_24dp.png | Bin 0 -> 273 bytes .../common/res/drawable-hdpi/ic_rx_videocam.png | Bin 0 -> 413 bytes .../common/res/drawable-hdpi/ic_tx_videocam.png | Bin 0 -> 370 bytes .../common/res/drawable-hdpi/ic_videocam.png | Bin 0 -> 269 bytes .../res/drawable-hdpi/ic_voicemail_avatar.png | Bin 0 -> 2856 bytes .../res/drawable-hdpi/list_activated_holo.9.png | Bin 0 -> 154 bytes .../res/drawable-hdpi/list_background_holo.9.png | Bin 0 -> 224 bytes .../res/drawable-hdpi/list_focused_holo.9.png | Bin 0 -> 235 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 158 bytes .../drawable-hdpi/list_pressed_holo_light.9.png | Bin 0 -> 159 bytes .../list_section_divider_holo_custom.9.png | Bin 0 -> 205 bytes .../common/res/drawable-hdpi/list_title_holo.9.png | Bin 0 -> 267 bytes .../drawable-ldrtl-hdpi/list_focused_holo.9.png | Bin 0 -> 234 bytes .../list_section_divider_holo_custom.9.png | Bin 0 -> 191 bytes .../res/drawable-ldrtl-hdpi/list_title_holo.9.png | Bin 0 -> 258 bytes .../drawable-ldrtl-mdpi/list_focused_holo.9.png | Bin 0 -> 234 bytes .../list_section_divider_holo_custom.9.png | Bin 0 -> 180 bytes .../res/drawable-ldrtl-mdpi/list_title_holo.9.png | Bin 0 -> 186 bytes .../list_activated_holo.9.png | Bin 0 -> 1666 bytes .../list_activated_holo.9.png | Bin 0 -> 1034 bytes .../list_activated_holo.9.png | Bin 0 -> 2486 bytes .../drawable-ldrtl-xhdpi/list_focused_holo.9.png | Bin 0 -> 234 bytes .../list_section_divider_holo_custom.9.png | Bin 0 -> 196 bytes .../res/drawable-ldrtl-xhdpi/list_title_holo.9.png | Bin 0 -> 255 bytes .../res/drawable-mdpi/list_activated_holo.9.png | Bin 0 -> 151 bytes .../res/drawable-mdpi/list_focused_holo.9.png | Bin 0 -> 235 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 155 bytes .../drawable-mdpi/list_pressed_holo_light.9.png | Bin 0 -> 158 bytes .../list_section_divider_holo_custom.9.png | Bin 0 -> 198 bytes .../list_activated_holo.9.png | Bin 0 -> 1659 bytes .../list_activated_holo.9.png | Bin 0 -> 1005 bytes .../list_activated_holo.9.png | Bin 0 -> 2478 bytes .../res/drawable-xhdpi/list_activated_holo.9.png | Bin 0 -> 158 bytes .../res/drawable-xhdpi/list_focused_holo.9.png | Bin 0 -> 235 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 162 bytes .../drawable-xhdpi/list_pressed_holo_light.9.png | Bin 0 -> 163 bytes .../list_section_divider_holo_custom.9.png | Bin 0 -> 210 bytes .../res/drawable-xxhdpi/list_activated_holo.9.png | Bin 0 -> 1140 bytes .../res/drawable-xxhdpi/list_focused_holo.9.png | Bin 0 -> 1147 bytes .../list_longpressed_holo_light.9.png | Bin 0 -> 1051 bytes .../drawable-xxhdpi/list_pressed_holo_light.9.png | Bin 0 -> 1051 bytes .../contacts/common/res/drawable/ic_back_arrow.xml | 20 ++ .../common/res/drawable/ic_call_and_share.xml | 22 ++ .../res/drawable/ic_person_add_tinted_24dp.xml | 20 ++ .../common/res/drawable/ic_search_add_contact.xml | 20 ++ .../common/res/drawable/ic_search_video_call.xml | 22 ++ ...t_selector_background_transition_holo_light.xml | 20 ++ .../common/res/layout/dialog_call_subject.xml | 2 +- .../common/res/layout/search_bar_expanded.xml | 4 +- .../contacts/common/res/values-af/strings.xml | 2 +- .../contacts/common/res/values-am/strings.xml | 2 +- .../contacts/common/res/values-ar/strings.xml | 2 +- .../contacts/common/res/values-az/strings.xml | 2 +- .../common/res/values-b+sr+Latn/strings.xml | 2 +- .../contacts/common/res/values-be/strings.xml | 2 +- .../contacts/common/res/values-bg/strings.xml | 2 +- .../contacts/common/res/values-bn/strings.xml | 2 +- .../contacts/common/res/values-bs/strings.xml | 2 +- .../contacts/common/res/values-ca/strings.xml | 2 +- .../contacts/common/res/values-cs/strings.xml | 2 +- .../contacts/common/res/values-da/strings.xml | 2 +- .../contacts/common/res/values-de/strings.xml | 2 +- .../contacts/common/res/values-el/strings.xml | 2 +- .../contacts/common/res/values-en-rAU/strings.xml | 2 +- .../contacts/common/res/values-en-rGB/strings.xml | 2 +- .../contacts/common/res/values-en-rIN/strings.xml | 2 +- .../contacts/common/res/values-es-rUS/strings.xml | 2 +- .../contacts/common/res/values-es/strings.xml | 2 +- .../contacts/common/res/values-et/strings.xml | 2 +- .../contacts/common/res/values-eu/strings.xml | 2 +- .../contacts/common/res/values-fa/strings.xml | 2 +- .../contacts/common/res/values-fi/strings.xml | 2 +- .../contacts/common/res/values-fr-rCA/strings.xml | 2 +- .../contacts/common/res/values-fr/strings.xml | 2 +- .../contacts/common/res/values-gl/strings.xml | 2 +- .../contacts/common/res/values-gu/strings.xml | 2 +- .../contacts/common/res/values-hi/strings.xml | 2 +- .../contacts/common/res/values-hr/strings.xml | 2 +- .../contacts/common/res/values-hu/strings.xml | 2 +- .../contacts/common/res/values-hy/strings.xml | 2 +- .../contacts/common/res/values-in/strings.xml | 2 +- .../contacts/common/res/values-is/strings.xml | 2 +- .../contacts/common/res/values-it/strings.xml | 2 +- .../contacts/common/res/values-iw/strings.xml | 2 +- .../contacts/common/res/values-ja/strings.xml | 2 +- .../contacts/common/res/values-ka/strings.xml | 2 +- .../contacts/common/res/values-kk/strings.xml | 2 +- .../contacts/common/res/values-km/strings.xml | 2 +- .../contacts/common/res/values-kn/strings.xml | 2 +- .../contacts/common/res/values-ko/strings.xml | 2 +- .../contacts/common/res/values-ky/strings.xml | 2 +- .../contacts/common/res/values-lo/strings.xml | 2 +- .../contacts/common/res/values-lt/strings.xml | 2 +- .../contacts/common/res/values-lv/strings.xml | 2 +- .../contacts/common/res/values-mk/strings.xml | 2 +- .../contacts/common/res/values-ml/strings.xml | 2 +- .../contacts/common/res/values-mn/strings.xml | 2 +- .../contacts/common/res/values-mr/strings.xml | 2 +- .../contacts/common/res/values-ms/strings.xml | 2 +- .../contacts/common/res/values-my/strings.xml | 2 +- .../contacts/common/res/values-nb/strings.xml | 2 +- .../contacts/common/res/values-ne/strings.xml | 2 +- .../contacts/common/res/values-nl/strings.xml | 2 +- .../contacts/common/res/values-no/strings.xml | 2 +- .../contacts/common/res/values-pa/strings.xml | 2 +- .../contacts/common/res/values-pl/strings.xml | 2 +- .../contacts/common/res/values-pt-rBR/strings.xml | 2 +- .../contacts/common/res/values-pt-rPT/strings.xml | 2 +- .../contacts/common/res/values-pt/strings.xml | 2 +- .../contacts/common/res/values-ro/strings.xml | 2 +- .../contacts/common/res/values-ru/strings.xml | 2 +- .../contacts/common/res/values-si/strings.xml | 2 +- .../contacts/common/res/values-sk/strings.xml | 2 +- .../contacts/common/res/values-sl/strings.xml | 2 +- .../contacts/common/res/values-sq/strings.xml | 2 +- .../contacts/common/res/values-sr/strings.xml | 2 +- .../contacts/common/res/values-sv/strings.xml | 2 +- .../contacts/common/res/values-sw/strings.xml | 2 +- .../contacts/common/res/values-ta/strings.xml | 2 +- .../contacts/common/res/values-te/strings.xml | 2 +- .../contacts/common/res/values-th/strings.xml | 2 +- .../contacts/common/res/values-tl/strings.xml | 2 +- .../contacts/common/res/values-tr/strings.xml | 2 +- .../contacts/common/res/values-uk/strings.xml | 2 +- .../contacts/common/res/values-ur/strings.xml | 2 +- .../contacts/common/res/values-uz/strings.xml | 2 +- .../contacts/common/res/values-vi/strings.xml | 2 +- .../contacts/common/res/values-zh-rCN/strings.xml | 2 +- .../contacts/common/res/values-zh-rHK/strings.xml | 2 +- .../contacts/common/res/values-zh-rTW/strings.xml | 2 +- .../contacts/common/res/values-zu/strings.xml | 2 +- .../com/android/contacts/common/res/values/ids.xml | 1 - .../android/contacts/common/res/values/strings.xml | 6 +- .../android/contacts/common/res/values/styles.xml | 1 + .../contacts/common/util/AccountFilterUtil.java | 42 ++++ .../android/contacts/common/util/BitmapUtil.java | 26 +++ .../contacts/common/util/ContactDisplayUtils.java | 12 +- .../android/contacts/common/util/DateUtils.java | 241 +++++++++++++++++++ .../common/util/MaterialColorMapUtils.java | 8 + .../contacts/common/util/NameConverter.java | 242 +++++++++++++++++++ .../android/contacts/common/util/StopWatch.java | 28 ++- .../common/util/TelephonyManagerUtils.java | 9 + .../contacts/common/util/TrafficStatsTags.java | 22 ++ 190 files changed, 2472 insertions(+), 397 deletions(-) create mode 100644 java/com/android/contacts/common/GeoUtil.java create mode 100644 java/com/android/contacts/common/format/FormatUtils.java create mode 100644 java/com/android/contacts/common/format/testing/SpannedTestUtils.java create mode 100644 java/com/android/contacts/common/location/CountryDetector.java create mode 100644 java/com/android/contacts/common/location/UpdateCountryService.java create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_ab_search.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_arrow_back_24dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_business_white_120dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_call_24dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_close_dk.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_create_24dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_group_white_24dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_history_white_drawable_24dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_info_outline_24dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_back.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_dk.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_lt.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_overflow_lt.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_dk.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_lt.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_remove_field_holo_light.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_dk.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_holo_light.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_lt.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_person_24dp.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_rx_videocam.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_tx_videocam.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_videocam.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/ic_voicemail_avatar.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/list_background_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/list_focused_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/list_longpressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/list_pressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/list_section_divider_holo_custom.9.png create mode 100644 java/com/android/contacts/common/res/drawable-hdpi/list_title_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_focused_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_title_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_focused_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_title_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png create mode 100644 java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_title_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-mdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-mdpi/list_focused_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-mdpi/list_longpressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable-mdpi/list_pressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable-mdpi/list_section_divider_holo_custom.9.png create mode 100644 java/com/android/contacts/common/res/drawable-sw600dp-hdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-sw600dp-mdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xhdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xhdpi/list_focused_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xhdpi/list_longpressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xhdpi/list_pressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xhdpi/list_section_divider_holo_custom.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xxhdpi/list_activated_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xxhdpi/list_focused_holo.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xxhdpi/list_longpressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable-xxhdpi/list_pressed_holo_light.9.png create mode 100644 java/com/android/contacts/common/res/drawable/ic_back_arrow.xml create mode 100644 java/com/android/contacts/common/res/drawable/ic_call_and_share.xml create mode 100644 java/com/android/contacts/common/res/drawable/ic_person_add_tinted_24dp.xml create mode 100644 java/com/android/contacts/common/res/drawable/ic_search_add_contact.xml create mode 100644 java/com/android/contacts/common/res/drawable/ic_search_video_call.xml create mode 100644 java/com/android/contacts/common/res/drawable/list_selector_background_transition_holo_light.xml create mode 100644 java/com/android/contacts/common/util/NameConverter.java create mode 100644 java/com/android/contacts/common/util/TrafficStatsTags.java (limited to 'java/com/android/contacts') diff --git a/java/com/android/contacts/common/ClipboardUtils.java b/java/com/android/contacts/common/ClipboardUtils.java index 3d7683941..9345b0f9c 100644 --- a/java/com/android/contacts/common/ClipboardUtils.java +++ b/java/com/android/contacts/common/ClipboardUtils.java @@ -24,6 +24,8 @@ import android.widget.Toast; public class ClipboardUtils { + private static final String TAG = "ClipboardUtils"; + private ClipboardUtils() {} /** diff --git a/java/com/android/contacts/common/ContactPhotoManager.java b/java/com/android/contacts/common/ContactPhotoManager.java index 3e1a78f63..169348b25 100644 --- a/java/com/android/contacts/common/ContactPhotoManager.java +++ b/java/com/android/contacts/common/ContactPhotoManager.java @@ -36,6 +36,14 @@ import com.android.dialer.util.PermissionsUtil; /** Asynchronously loads contact photos and maintains a cache of photos. */ public abstract class ContactPhotoManager implements ComponentCallbacks2 { + /** Contact type constants used for default letter images */ + public static final int TYPE_PERSON = LetterTileDrawable.TYPE_PERSON; + + public static final int TYPE_SPAM = LetterTileDrawable.TYPE_SPAM; + public static final int TYPE_BUSINESS = LetterTileDrawable.TYPE_BUSINESS; + public static final int TYPE_VOICEMAIL = LetterTileDrawable.TYPE_VOICEMAIL; + public static final int TYPE_DEFAULT = LetterTileDrawable.TYPE_DEFAULT; + public static final int TYPE_GENERIC_AVATAR = LetterTileDrawable.TYPE_GENERIC_AVATAR; /** Scale and offset default constants used for default letter images */ public static final float SCALE_DEFAULT = 1.0f; @@ -80,7 +88,7 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 { if (!TextUtils.isEmpty(request.identifier)) { builder.appendQueryParameter(IDENTIFIER_PARAM_KEY, request.identifier); } - if (request.contactType != LetterTileDrawable.TYPE_DEFAULT) { + if (request.contactType != TYPE_DEFAULT) { builder.appendQueryParameter(CONTACT_TYPE_PARAM_KEY, String.valueOf(request.contactType)); } if (request.scale != SCALE_DEFAULT) { @@ -106,7 +114,7 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 { public static String appendBusinessContactType(String photoUrl) { Uri uri = Uri.parse(photoUrl); Builder builder = uri.buildUpon(); - builder.encodedFragment(String.valueOf(LetterTileDrawable.TYPE_BUSINESS)); + builder.encodedFragment(String.valueOf(TYPE_BUSINESS)); return builder.build().toString(); } @@ -139,7 +147,7 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 { String encodedFragment = photoUri.getEncodedFragment(); return !TextUtils.isEmpty(encodedFragment) - && encodedFragment.equals(String.valueOf(LetterTileDrawable.TYPE_BUSINESS)); + && encodedFragment.equals(String.valueOf(TYPE_BUSINESS)); } protected static DefaultImageRequest getDefaultImageRequestFromUri(Uri uri) { @@ -370,7 +378,7 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 { * be returned. */ public static final DefaultImageRequest EMPTY_DEFAULT_BUSINESS_IMAGE_REQUEST = - new DefaultImageRequest(null, null, LetterTileDrawable.TYPE_BUSINESS, false); + new DefaultImageRequest(null, null, TYPE_BUSINESS, false); /** * Used to indicate that a circular drawable that represents a contact without any contact * details should be returned. @@ -382,7 +390,7 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 { * should be returned. */ public static final DefaultImageRequest EMPTY_CIRCULAR_BUSINESS_IMAGE_REQUEST = - new DefaultImageRequest(null, null, LetterTileDrawable.TYPE_BUSINESS, true); + new DefaultImageRequest(null, null, TYPE_BUSINESS, true); /** The contact's display name. The display name is used to */ public String displayName; /** @@ -395,9 +403,10 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 { /** * The type of this contact. This contact type may be used to decide the kind of image to use in * the case where a unique letter cannot be generated from the contact's display name and - * identifier. + * identifier. See: {@link #TYPE_PERSON} {@link #TYPE_BUSINESS} {@link #TYPE_PERSON} {@link + * #TYPE_DEFAULT} */ - public @LetterTileDrawable.ContactType int contactType = LetterTileDrawable.TYPE_DEFAULT; + public int contactType = TYPE_DEFAULT; /** * The amount to scale the letter or bitmap to, as a ratio of its default size (from a range of * 0.0f to 2.0f). The default value is 1.0f. @@ -420,13 +429,7 @@ public abstract class ContactPhotoManager implements ComponentCallbacks2 { public DefaultImageRequest() {} public DefaultImageRequest(String displayName, String identifier, boolean isCircular) { - this( - displayName, - identifier, - LetterTileDrawable.TYPE_DEFAULT, - SCALE_DEFAULT, - OFFSET_DEFAULT, - isCircular); + this(displayName, identifier, TYPE_DEFAULT, SCALE_DEFAULT, OFFSET_DEFAULT, isCircular); } public DefaultImageRequest( diff --git a/java/com/android/contacts/common/ContactPhotoManagerImpl.java b/java/com/android/contacts/common/ContactPhotoManagerImpl.java index 28ecf3421..2e6ff9fdc 100644 --- a/java/com/android/contacts/common/ContactPhotoManagerImpl.java +++ b/java/com/android/contacts/common/ContactPhotoManagerImpl.java @@ -53,9 +53,9 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import com.android.contacts.common.util.BitmapUtil; +import com.android.contacts.common.util.TrafficStatsTags; import com.android.contacts.common.util.UriUtils; import com.android.dialer.common.LogUtil; -import com.android.dialer.constants.TrafficStatsTags; import com.android.dialer.util.PermissionsUtil; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -634,9 +634,8 @@ class ContactPhotoManagerImpl extends ContactPhotoManager implements Callback { } return true; } - default: - return false; } + return false; } public void ensureLoaderThread() { diff --git a/java/com/android/contacts/common/ContactStatusUtil.java b/java/com/android/contacts/common/ContactStatusUtil.java index c5347e778..97d84c876 100644 --- a/java/com/android/contacts/common/ContactStatusUtil.java +++ b/java/com/android/contacts/common/ContactStatusUtil.java @@ -23,6 +23,8 @@ import android.provider.ContactsContract.StatusUpdates; /** Provides static function to get default contact status message. */ public class ContactStatusUtil { + private static final String TAG = "ContactStatusUtil"; + public static String getStatusString(Context context, int presence) { Resources resources = context.getResources(); switch (presence) { diff --git a/java/com/android/contacts/common/ContactsUtils.java b/java/com/android/contacts/common/ContactsUtils.java index 66ccc90e7..60af44b9a 100644 --- a/java/com/android/contacts/common/ContactsUtils.java +++ b/java/com/android/contacts/common/ContactsUtils.java @@ -16,17 +16,184 @@ package com.android.contacts.common; -import android.provider.ContactsContract.Contacts; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract.CommonDataKinds.Im; +import android.provider.ContactsContract.DisplayPhoto; import android.support.annotation.IntDef; +import android.text.TextUtils; +import android.util.Pair; +import com.android.contacts.common.compat.ContactsCompat; import com.android.contacts.common.compat.DirectoryCompat; +import com.android.contacts.common.model.AccountTypeManager; +import com.android.contacts.common.model.account.AccountWithDataSet; +import com.android.contacts.common.model.dataitem.ImDataItem; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.List; public class ContactsUtils { // Telecomm related schemes are in CallUtil + public static final String SCHEME_IMTO = "imto"; + public static final String SCHEME_MAILTO = "mailto"; + public static final String SCHEME_SMSTO = "smsto"; public static final long USER_TYPE_CURRENT = 0; public static final long USER_TYPE_WORK = 1; + private static final String TAG = "ContactsUtils"; + private static final int DEFAULT_THUMBNAIL_SIZE = 96; + private static int sThumbnailSize = -1; + + /** + * This looks up the provider name defined in ProviderNames from the predefined IM protocol id. + * This is used for interacting with the IM application. + * + * @param protocol the protocol ID + * @return the provider name the IM app uses for the given protocol, or null if no provider is + * defined for the given protocol + * @hide + */ + public static String lookupProviderNameFromId(int protocol) { + switch (protocol) { + case Im.PROTOCOL_GOOGLE_TALK: + return ProviderNames.GTALK; + case Im.PROTOCOL_AIM: + return ProviderNames.AIM; + case Im.PROTOCOL_MSN: + return ProviderNames.MSN; + case Im.PROTOCOL_YAHOO: + return ProviderNames.YAHOO; + case Im.PROTOCOL_ICQ: + return ProviderNames.ICQ; + case Im.PROTOCOL_JABBER: + return ProviderNames.JABBER; + case Im.PROTOCOL_SKYPE: + return ProviderNames.SKYPE; + case Im.PROTOCOL_QQ: + return ProviderNames.QQ; + } + return null; + } + + /** + * Test if the given {@link CharSequence} contains any graphic characters, first checking {@link + * TextUtils#isEmpty(CharSequence)} to handle null. + */ + public static boolean isGraphic(CharSequence str) { + return !TextUtils.isEmpty(str) && TextUtils.isGraphic(str); + } + + /** Returns true if two objects are considered equal. Two null references are equal here. */ + public static boolean areObjectsEqual(Object a, Object b) { + return a == b || (a != null && a.equals(b)); + } + + /** Returns true if two {@link Intent}s are both null, or have the same action. */ + public static final boolean areIntentActionEqual(Intent a, Intent b) { + if (a == b) { + return true; + } + if (a == null || b == null) { + return false; + } + return TextUtils.equals(a.getAction(), b.getAction()); + } + + public static boolean areGroupWritableAccountsAvailable(Context context) { + final List accounts = + AccountTypeManager.getInstance(context).getGroupWritableAccounts(); + return !accounts.isEmpty(); + } + + /** + * Returns the size (width and height) of thumbnail pictures as configured in the provider. This + * can safely be called from the UI thread, as the provider can serve this without performing a + * database access + */ + public static int getThumbnailSize(Context context) { + if (sThumbnailSize == -1) { + final Cursor c = + context + .getContentResolver() + .query( + DisplayPhoto.CONTENT_MAX_DIMENSIONS_URI, + new String[] {DisplayPhoto.THUMBNAIL_MAX_DIM}, + null, + null, + null); + if (c != null) { + try { + if (c.moveToFirst()) { + sThumbnailSize = c.getInt(0); + } + } finally { + c.close(); + } + } + } + return sThumbnailSize != -1 ? sThumbnailSize : DEFAULT_THUMBNAIL_SIZE; + } + + private static Intent getCustomImIntent(ImDataItem im, int protocol) { + String host = im.getCustomProtocol(); + final String data = im.getData(); + if (TextUtils.isEmpty(data)) { + return null; + } + if (protocol != Im.PROTOCOL_CUSTOM) { + // Try bringing in a well-known host for specific protocols + host = ContactsUtils.lookupProviderNameFromId(protocol); + } + if (TextUtils.isEmpty(host)) { + return null; + } + final String authority = host.toLowerCase(); + final Uri imUri = + new Uri.Builder().scheme(SCHEME_IMTO).authority(authority).appendPath(data).build(); + final Intent intent = new Intent(Intent.ACTION_SENDTO, imUri); + return intent; + } + + /** + * Returns the proper Intent for an ImDatItem. If available, a secondary intent is stored in the + * second Pair slot + */ + public static Pair buildImIntent(Context context, ImDataItem im) { + Intent intent = null; + Intent secondaryIntent = null; + final boolean isEmail = im.isCreatedFromEmail(); + + if (!isEmail && !im.isProtocolValid()) { + return new Pair<>(null, null); + } + + final String data = im.getData(); + if (TextUtils.isEmpty(data)) { + return new Pair<>(null, null); + } + + final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol(); + + if (protocol == Im.PROTOCOL_GOOGLE_TALK) { + final int chatCapability = im.getChatCapability(); + if ((chatCapability & Im.CAPABILITY_HAS_CAMERA) != 0) { + intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); + secondaryIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call")); + } else if ((chatCapability & Im.CAPABILITY_HAS_VOICE) != 0) { + // Allow Talking and Texting + intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); + secondaryIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?call")); + } else { + intent = new Intent(Intent.ACTION_SENDTO, Uri.parse("xmpp:" + data + "?message")); + } + } else { + // Build an IM Intent + intent = getCustomImIntent(im, protocol); + } + return new Pair<>(intent, secondaryIntent); + } /** * Determine UserType from directory id and contact id. @@ -65,13 +232,27 @@ public class ContactsUtils { : USER_TYPE_CURRENT; } // Only check contact id if directory id is null - if (contactId != null && contactId != 0L && Contacts.isEnterpriseContactId(contactId)) { + if (contactId != null && contactId != 0L && ContactsCompat.isEnterpriseContactId(contactId)) { return USER_TYPE_WORK; } else { return USER_TYPE_CURRENT; } } + // 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"; + } + /** * UserType indicates the user type of the contact. If the contact is from Work User (Work Profile * in Android Multi-User System), it's {@link #USER_TYPE_WORK}, otherwise, {@link diff --git a/java/com/android/contacts/common/GeoUtil.java b/java/com/android/contacts/common/GeoUtil.java new file mode 100644 index 000000000..50b0cd9e3 --- /dev/null +++ b/java/com/android/contacts/common/GeoUtil.java @@ -0,0 +1,55 @@ +/* + * 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.common; + +import android.app.Application; +import android.content.Context; +import com.android.contacts.common.location.CountryDetector; +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber; +import com.google.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder; +import java.util.Locale; + +/** Static methods related to Geo. */ +public class GeoUtil { + + /** + * Returns the country code of the country the user is currently in. Before calling this method, + * make sure that {@link CountryDetector#initialize(Context)} has already been called in {@link + * Application#onCreate()}. + * + * @return The ISO 3166-1 two letters country code of the country the user is in. + */ + public static String getCurrentCountryIso(Context context) { + // The {@link CountryDetector} should never return null so this is safe to return as-is. + return CountryDetector.getInstance(context).getCurrentCountryIso(); + } + + public static String getGeocodedLocationFor(Context context, String phoneNumber) { + final PhoneNumberOfflineGeocoder geocoder = PhoneNumberOfflineGeocoder.getInstance(); + final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + try { + final Phonenumber.PhoneNumber structuredPhoneNumber = + phoneNumberUtil.parse(phoneNumber, getCurrentCountryIso(context)); + final Locale locale = context.getResources().getConfiguration().locale; + return geocoder.getDescriptionForNumber(structuredPhoneNumber, locale); + } catch (NumberParseException e) { + return null; + } + } +} diff --git a/java/com/android/contacts/common/MoreContactUtils.java b/java/com/android/contacts/common/MoreContactUtils.java index 26241b34f..028f89971 100644 --- a/java/com/android/contacts/common/MoreContactUtils.java +++ b/java/com/android/contacts/common/MoreContactUtils.java @@ -16,6 +16,7 @@ package com.android.contacts.common; +import android.content.Context; import android.content.Intent; import android.graphics.Rect; import android.net.Uri; @@ -23,6 +24,7 @@ import android.provider.ContactsContract; import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import android.view.View; +import android.widget.TextView; import com.android.contacts.common.model.account.AccountType; import com.google.i18n.phonenumbers.NumberParseException; import com.google.i18n.phonenumbers.PhoneNumberUtil; @@ -191,6 +193,40 @@ public class MoreContactUtils { return rect; } + /** + * Returns a header view based on the R.layout.list_separator, where the containing {@link + * android.widget.TextView} is set using the given textResourceId. + */ + public static TextView createHeaderView(Context context, int textResourceId) { + final TextView textView = (TextView) View.inflate(context, R.layout.list_separator, null); + textView.setText(context.getString(textResourceId)); + return textView; + } + + /** + * Set the top padding on the header view dynamically, based on whether the header is in the first + * row or not. + */ + public static void setHeaderViewBottomPadding( + Context context, TextView textView, boolean isFirstRow) { + final int topPadding; + if (isFirstRow) { + topPadding = + (int) + context + .getResources() + .getDimension(R.dimen.frequently_contacted_title_top_margin_when_first_row); + } else { + topPadding = + (int) context.getResources().getDimension(R.dimen.frequently_contacted_title_top_margin); + } + textView.setPaddingRelative( + textView.getPaddingStart(), + topPadding, + textView.getPaddingEnd(), + textView.getPaddingBottom()); + } + /** * Returns the intent to launch for the given invitable account type and contact lookup URI. This * will return null if the account type is not invitable (i.e. there is no {@link diff --git a/java/com/android/contacts/common/compat/ContactsCompat.java b/java/com/android/contacts/common/compat/ContactsCompat.java index e0c9b7e53..39d0b55d3 100644 --- a/java/com/android/contacts/common/compat/ContactsCompat.java +++ b/java/com/android/contacts/common/compat/ContactsCompat.java @@ -21,6 +21,7 @@ import android.os.Build.VERSION; import android.os.Build.VERSION_CODES; import android.provider.ContactsContract; import android.provider.ContactsContract.Contacts; +import com.android.dialer.compat.CompatUtils; /** Compatibility class for {@link ContactsContract.Contacts} */ public class ContactsCompat { @@ -28,6 +29,8 @@ public class ContactsCompat { // TODO: Use N APIs private static final Uri ENTERPRISE_CONTENT_FILTER_URI = Uri.withAppendedPath(Contacts.CONTENT_URI, "filter_enterprise"); + // Copied from ContactsContract.Contacts#ENTERPRISE_CONTACT_ID_BASE, which is hidden. + private static final long ENTERPRISE_CONTACT_ID_BASE = 1000000000; /** Not instantiable. */ private ContactsCompat() {} @@ -38,4 +41,17 @@ public class ContactsCompat { } return Contacts.CONTENT_FILTER_URI; } + + /** + * Return {@code true} if a contact ID is from the contacts provider on the enterprise profile. + */ + public static boolean isEnterpriseContactId(long contactId) { + if (CompatUtils.isLollipopCompatible()) { + return Contacts.isEnterpriseContactId(contactId); + } else { + // copied from ContactsContract.Contacts.isEnterpriseContactId + return (contactId >= ENTERPRISE_CONTACT_ID_BASE) + && (contactId < ContactsContract.Profile.MIN_ID); + } + } } diff --git a/java/com/android/contacts/common/compat/PhoneAccountCompat.java b/java/com/android/contacts/common/compat/PhoneAccountCompat.java index aa22c6861..6a24ec033 100644 --- a/java/com/android/contacts/common/compat/PhoneAccountCompat.java +++ b/java/com/android/contacts/common/compat/PhoneAccountCompat.java @@ -20,10 +20,33 @@ import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.support.annotation.Nullable; import android.telecom.PhoneAccount; +import android.util.Log; +import com.android.dialer.compat.CompatUtils; /** Compatiblity class for {@link android.telecom.PhoneAccount} */ public class PhoneAccountCompat { + private static final String TAG = PhoneAccountCompat.class.getSimpleName(); + + /** + * Gets the {@link Icon} associated with the given {@link PhoneAccount} + * + * @param phoneAccount the PhoneAccount from which to retrieve the Icon + * @return the Icon, or null + */ + @Nullable + public static Icon getIcon(@Nullable PhoneAccount phoneAccount) { + if (phoneAccount == null) { + return null; + } + + if (CompatUtils.isMarshmallowCompatible()) { + return phoneAccount.getIcon(); + } + + return null; + } + /** * Builds and returns an icon {@code Drawable} to represent this {@code PhoneAccount} in a user * interface. @@ -38,16 +61,44 @@ public class PhoneAccountCompat { if (phoneAccount == null || context == null) { return null; } - return createIconDrawableMarshmallow(phoneAccount, context); + + if (CompatUtils.isMarshmallowCompatible()) { + return createIconDrawableMarshmallow(phoneAccount, context); + } + + if (CompatUtils.isLollipopMr1Compatible()) { + return createIconDrawableLollipopMr1(phoneAccount, context); + } + return null; } @Nullable private static Drawable createIconDrawableMarshmallow( PhoneAccount phoneAccount, Context context) { - Icon accountIcon = phoneAccount.getIcon(); + Icon accountIcon = getIcon(phoneAccount); if (accountIcon == null) { return null; } return accountIcon.loadDrawable(context); } + + @Nullable + private static Drawable createIconDrawableLollipopMr1( + PhoneAccount phoneAccount, Context context) { + try { + return (Drawable) + PhoneAccount.class + .getMethod("createIconDrawable", Context.class) + .invoke(phoneAccount, context); + } catch (ReflectiveOperationException e) { + return null; + } catch (Throwable t) { + Log.e( + TAG, + "Unexpected exception when attempting to call " + + "android.telecom.PhoneAccount#createIconDrawable", + t); + return null; + } + } } diff --git a/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java index a6cfe07cd..960b340d8 100644 --- a/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java +++ b/java/com/android/contacts/common/compat/PhoneNumberUtilsCompat.java @@ -17,7 +17,13 @@ package com.android.contacts.common.compat; import android.telephony.PhoneNumberUtils; +import android.text.Spannable; +import android.text.TextUtils; import android.text.style.TtsSpan; +import com.android.dialer.compat.CompatUtils; +import com.google.i18n.phonenumbers.NumberParseException; +import com.google.i18n.phonenumbers.PhoneNumberUtil; +import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber; /** * This class contains static utility methods extracted from PhoneNumberUtils, and the methods were @@ -31,16 +37,138 @@ public class PhoneNumberUtilsCompat { /** Not instantiable. */ private PhoneNumberUtilsCompat() {} + public static String normalizeNumber(String phoneNumber) { + if (CompatUtils.isLollipopCompatible()) { + return PhoneNumberUtils.normalizeNumber(phoneNumber); + } else { + return normalizeNumberInternal(phoneNumber); + } + } + + /** Implementation copied from {@link PhoneNumberUtils#normalizeNumber} */ + private static String normalizeNumberInternal(String phoneNumber) { + if (TextUtils.isEmpty(phoneNumber)) { + return ""; + } + StringBuilder sb = new StringBuilder(); + int len = phoneNumber.length(); + for (int i = 0; i < len; i++) { + char c = phoneNumber.charAt(i); + // Character.digit() supports ASCII and Unicode digits (fullwidth, Arabic-Indic, etc.) + int digit = Character.digit(c, 10); + if (digit != -1) { + sb.append(digit); + } else if (sb.length() == 0 && c == '+') { + sb.append(c); + } else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { + return normalizeNumber(PhoneNumberUtils.convertKeypadLettersToDigits(phoneNumber)); + } + } + return sb.toString(); + } + public static String formatNumber( String phoneNumber, String phoneNumberE164, String defaultCountryIso) { + if (CompatUtils.isLollipopCompatible()) { return PhoneNumberUtils.formatNumber(phoneNumber, phoneNumberE164, defaultCountryIso); + } else { + // This method was deprecated in API level 21, so it's only used on pre-L SDKs. + return PhoneNumberUtils.formatNumber(phoneNumber); + } } public static CharSequence createTtsSpannable(CharSequence phoneNumber) { - return PhoneNumberUtils.createTtsSpannable(phoneNumber); + if (CompatUtils.isMarshmallowCompatible()) { + return PhoneNumberUtils.createTtsSpannable(phoneNumber); + } else { + return createTtsSpannableInternal(phoneNumber); + } } public static TtsSpan createTtsSpan(String phoneNumber) { - return PhoneNumberUtils.createTtsSpan(phoneNumber); + if (CompatUtils.isMarshmallowCompatible()) { + return PhoneNumberUtils.createTtsSpan(phoneNumber); + } else if (CompatUtils.isLollipopCompatible()) { + return createTtsSpanLollipop(phoneNumber); + } else { + return null; + } + } + + /** Copied from {@link PhoneNumberUtils#createTtsSpannable} */ + private static CharSequence createTtsSpannableInternal(CharSequence phoneNumber) { + if (phoneNumber == null) { + return null; + } + Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber); + addTtsSpanInternal(spannable, 0, spannable.length()); + return spannable; + } + + /** Compat method for addTtsSpan, see {@link PhoneNumberUtils#addTtsSpan} */ + public static void addTtsSpan(Spannable s, int start, int endExclusive) { + if (CompatUtils.isMarshmallowCompatible()) { + PhoneNumberUtils.addTtsSpan(s, start, endExclusive); + } else { + addTtsSpanInternal(s, start, endExclusive); + } + } + + /** Copied from {@link PhoneNumberUtils#addTtsSpan} */ + private static void addTtsSpanInternal(Spannable s, int start, int endExclusive) { + s.setSpan( + createTtsSpan(s.subSequence(start, endExclusive).toString()), + start, + endExclusive, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + /** Copied from {@link PhoneNumberUtils#createTtsSpan} */ + private static TtsSpan createTtsSpanLollipop(String phoneNumberString) { + if (phoneNumberString == null) { + return null; + } + + // Parse the phone number + final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance(); + PhoneNumber phoneNumber = null; + try { + // Don't supply a defaultRegion so this fails for non-international numbers because + // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already + // present + phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null); + } catch (NumberParseException ignored) { + } + + // Build a telephone tts span + final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder(); + if (phoneNumber == null) { + // Strip separators otherwise TalkBack will be silent + // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel) + builder.setNumberParts(splitAtNonNumerics(phoneNumberString)); + } else { + if (phoneNumber.hasCountryCode()) { + builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode())); + } + builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber())); + } + return builder.build(); + } + + /** + * Split a phone number using spaces, ignoring anything that is not a digit + * + * @param number A {@code CharSequence} before splitting, e.g., "+20(123)-456#" + * @return A {@code String} after splitting, e.g., "20 123 456". + */ + private static String splitAtNonNumerics(CharSequence number) { + StringBuilder sb = new StringBuilder(number.length()); + for (int i = 0; i < number.length(); i++) { + sb.append(PhoneNumberUtils.isISODigit(number.charAt(i)) ? number.charAt(i) : " "); + } + // It is very important to remove extra spaces. At time of writing, any leading or trailing + // spaces, or any sequence of more than one space, will confuse TalkBack and cause the TTS + // span to be non-functional! + return sb.toString().replaceAll(" +", " ").trim(); } } diff --git a/java/com/android/contacts/common/compat/TelephonyManagerCompat.java b/java/com/android/contacts/common/compat/TelephonyManagerCompat.java index 4a16fb855..7e4803ca5 100644 --- a/java/com/android/contacts/common/compat/TelephonyManagerCompat.java +++ b/java/com/android/contacts/common/compat/TelephonyManagerCompat.java @@ -27,6 +27,7 @@ import android.telecom.PhoneAccountHandle; import android.telephony.TelephonyManager; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; +import com.android.dialer.compat.CompatUtils; import java.lang.reflect.InvocationTargetException; public class TelephonyManagerCompat { @@ -47,6 +48,31 @@ public class TelephonyManagerCompat { private static final String SECRET_CODE_ACTION = "android.provider.Telephony.SECRET_CODE"; + /** + * @param telephonyManager The telephony manager instance to use for method calls. + * @return true if the current device is "voice capable". + *

"Voice capable" means that this device supports circuit-switched (i.e. voice) phone + * calls over the telephony network, and is allowed to display the in-call UI while a cellular + * voice call is active. This will be false on "data only" devices which can't make voice + * calls and don't support any in-call UI. + *

Note: the meaning of this flag is subtly different from the + * PackageManager.FEATURE_TELEPHONY system feature, which is available on any device with a + * telephony radio, even if the device is data-only. + */ + public static boolean isVoiceCapable(@Nullable TelephonyManager telephonyManager) { + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isLollipopMr1Compatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isVoiceCapable")) { + // isVoiceCapable was unhidden in L-MR1 + return telephonyManager.isVoiceCapable(); + } + final int phoneType = telephonyManager.getPhoneType(); + return phoneType == TelephonyManager.PHONE_TYPE_CDMA + || phoneType == TelephonyManager.PHONE_TYPE_GSM; + } + /** * Returns the number of phones available. Returns 1 for Single standby mode (Single SIM * functionality) Returns 2 for Dual standby mode.(Dual SIM functionality) @@ -59,7 +85,31 @@ public class TelephonyManagerCompat { if (telephonyManager == null) { return 1; } - return telephonyManager.getPhoneCount(); + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "getPhoneCount")) { + return telephonyManager.getPhoneCount(); + } + return 1; + } + + /** + * Returns the unique device ID of a subscription, for example, the IMEI for GSM and the MEID for + * CDMA phones. Return null if device ID is not available. + * + *

Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} + * + * @param telephonyManager The telephony manager instance to use for method calls. + * @param slotId of which deviceID is returned + */ + public static String getDeviceId(@Nullable TelephonyManager telephonyManager, int slotId) { + if (telephonyManager == null) { + return null; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "getDeviceId", Integer.class)) { + return telephonyManager.getDeviceId(slotId); + } + return null; } /** @@ -69,7 +119,14 @@ public class TelephonyManagerCompat { * @return {@code true} if the device supports TTY mode, and {@code false} otherwise. */ public static boolean isTtyModeSupported(@Nullable TelephonyManager telephonyManager) { - return telephonyManager != null && telephonyManager.isTtyModeSupported(); + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELEPHONY_MANAGER_CLASS, "isTtyModeSupported")) { + return telephonyManager.isTtyModeSupported(); + } + return false; } /** @@ -81,7 +138,15 @@ public class TelephonyManagerCompat { */ public static boolean isHearingAidCompatibilitySupported( @Nullable TelephonyManager telephonyManager) { - return telephonyManager != null && telephonyManager.isHearingAidCompatibilitySupported(); + if (telephonyManager == null) { + return false; + } + if (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELEPHONY_MANAGER_CLASS, "isHearingAidCompatibilitySupported")) { + return telephonyManager.isHearingAidCompatibilitySupported(); + } + return false; } /** diff --git a/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java index 43eae589d..5687f6fbf 100644 --- a/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java +++ b/java/com/android/contacts/common/compat/telecom/TelecomManagerCompat.java @@ -15,18 +15,274 @@ */ package com.android.contacts.common.compat.telecom; +import android.app.Activity; +import android.content.Intent; +import android.net.Uri; import android.support.annotation.Nullable; +import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; import android.telecom.TelecomManager; +import android.telephony.PhoneNumberUtils; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import com.android.dialer.compat.CompatUtils; +import java.util.ArrayList; +import java.util.List; /** Compatibility class for {@link android.telecom.TelecomManager}. */ public class TelecomManagerCompat { + public static final String TELECOM_MANAGER_CLASS = "android.telecom.TelecomManager"; + // TODO: remove once this is available in android.telecom.Call // b/33779976 public static final String EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS = "android.telecom.extra.LAST_EMERGENCY_CALLBACK_TIME_MILLIS"; + /** + * Places a new outgoing call to the provided address using the system telecom service with the + * specified intent. + * + * @param activity {@link Activity} used to start another activity for the given intent + * @param telecomManager the {@link TelecomManager} used to place a call, if possible + * @param intent the intent for the call + */ + public static void placeCall( + @Nullable Activity activity, + @Nullable TelecomManager telecomManager, + @Nullable Intent intent) { + if (activity == null || telecomManager == null || intent == null) { + return; + } + if (CompatUtils.isMarshmallowCompatible()) { + telecomManager.placeCall(intent.getData(), intent.getExtras()); + return; + } + activity.startActivityForResult(intent, 0); + } + + /** + * Get the URI for running an adn query. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @param accountHandle The handle for the account to derive an adn query URI for or {@code null} + * to return a URI which will use the default account. + * @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount} for + * the the content retrieve. + */ + public static Uri getAdnUriForPhoneAccount( + @Nullable TelecomManager telecomManager, PhoneAccountHandle accountHandle) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getAdnUriForPhoneAccount", PhoneAccountHandle.class))) { + return telecomManager.getAdnUriForPhoneAccount(accountHandle); + } + return Uri.parse("content://icc/adn"); + } + + /** + * Returns a list of {@link PhoneAccountHandle}s which can be used to make and receive phone + * calls. The returned list includes only those accounts which have been explicitly enabled by the + * user. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @return A list of PhoneAccountHandle objects. + */ + public static List getCallCapablePhoneAccounts( + @Nullable TelecomManager telecomManager) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getCallCapablePhoneAccounts"))) { + return telecomManager.getCallCapablePhoneAccounts(); + } + return new ArrayList<>(); + } + + /** + * Used to determine the currently selected default dialer package. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @return package name for the default dialer package or null if no package has been selected as + * the default dialer. + */ + @Nullable + public static String getDefaultDialerPackage(@Nullable TelecomManager telecomManager) { + if (telecomManager != null && CompatUtils.isDefaultDialerCompatible()) { + return telecomManager.getDefaultDialerPackage(); + } + return null; + } + + /** + * Return the {@link PhoneAccount} which will be used to place outgoing calls to addresses with + * the specified {@code uriScheme}. This PhoneAccount will always be a member of the list which is + * returned from invoking {@link TelecomManager#getCallCapablePhoneAccounts()}. The specific + * account returned depends on the following priorities: + * + *

1. If the user-selected default PhoneAccount supports the specified scheme, it will be + * returned. 2. If there exists only one PhoneAccount that supports the specified scheme, it will + * be returned. + * + *

If no PhoneAccount fits the criteria above, this method will return {@code null}. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @param uriScheme The URI scheme. + * @return The {@link PhoneAccountHandle} corresponding to the account to be used. + */ + @Nullable + public static PhoneAccountHandle getDefaultOutgoingPhoneAccount( + @Nullable TelecomManager telecomManager, @Nullable String uriScheme) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getDefaultOutgoingPhoneAccount", String.class))) { + return telecomManager.getDefaultOutgoingPhoneAccount(uriScheme); + } + return null; + } + + /** + * Return the line 1 phone number for given phone account. + * + * @param telecomManager the {@link TelecomManager} to use in the event that {@link + * TelecomManager#getLine1Number(PhoneAccountHandle)} is available + * @param telephonyManager the {@link TelephonyManager} to use if TelecomManager#getLine1Number is + * unavailable + * @param phoneAccountHandle the phoneAccountHandle upon which to check the line one number + * @return the line one number + */ + @Nullable + public static String getLine1Number( + @Nullable TelecomManager telecomManager, + @Nullable TelephonyManager telephonyManager, + @Nullable PhoneAccountHandle phoneAccountHandle) { + if (telecomManager != null && CompatUtils.isMarshmallowCompatible()) { + return telecomManager.getLine1Number(phoneAccountHandle); + } + if (telephonyManager != null) { + return telephonyManager.getLine1Number(); + } + return null; + } + + /** + * Return whether a given phone number is the configured voicemail number for a particular phone + * account. + * + * @param telecomManager the {@link TelecomManager} to use for checking the number. + * @param accountHandle The handle for the account to check the voicemail number against + * @param number The number to look up. + */ + public static boolean isVoiceMailNumber( + @Nullable TelecomManager telecomManager, + @Nullable PhoneAccountHandle accountHandle, + @Nullable String number) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, + "isVoiceMailNumber", + PhoneAccountHandle.class, + String.class))) { + return telecomManager.isVoiceMailNumber(accountHandle, number); + } + return PhoneNumberUtils.isVoiceMailNumber(number); + } + + /** + * Return the {@link PhoneAccount} for a specified {@link PhoneAccountHandle}. Object includes + * resources which can be used in a user interface. + * + * @param telecomManager the {@link TelecomManager} used for method calls, if possible. + * @param account The {@link PhoneAccountHandle}. + * @return The {@link PhoneAccount} object or null if it doesn't exist. + */ + @Nullable + public static PhoneAccount getPhoneAccount( + @Nullable TelecomManager telecomManager, @Nullable PhoneAccountHandle accountHandle) { + if (telecomManager != null + && (CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getPhoneAccount", PhoneAccountHandle.class))) { + return telecomManager.getPhoneAccount(accountHandle); + } + return null; + } + + /** + * Return the voicemail number for a given phone account. + * + * @param telecomManager The {@link TelecomManager} object to use for retrieving the voicemail + * number if accountHandle is specified. + * @param telephonyManager The {@link TelephonyManager} object to use for retrieving the voicemail + * number if accountHandle is null. + * @param accountHandle The handle for the phone account. + * @return The voicemail number for the phone account, and {@code null} if one has not been + * configured. + */ + @Nullable + public static String getVoiceMailNumber( + @Nullable TelecomManager telecomManager, + @Nullable TelephonyManager telephonyManager, + @Nullable PhoneAccountHandle accountHandle) { + if (telecomManager != null + && (CompatUtils.isMethodAvailable( + TELECOM_MANAGER_CLASS, "getVoiceMailNumber", PhoneAccountHandle.class))) { + return telecomManager.getVoiceMailNumber(accountHandle); + } else if (telephonyManager != null) { + return telephonyManager.getVoiceMailNumber(); + } + return null; + } + + /** + * Processes the specified dial string as an MMI code. MMI codes are any sequence of characters + * entered into the dialpad that contain a "*" or "#". Some of these sequences launch special + * behavior through handled by Telephony. + * + * @param telecomManager The {@link TelecomManager} object to use for handling MMI. + * @param dialString The digits to dial. + * @return {@code true} if the digits were processed as an MMI code, {@code false} otherwise. + */ + public static boolean handleMmi( + @Nullable TelecomManager telecomManager, + @Nullable String dialString, + @Nullable PhoneAccountHandle accountHandle) { + if (telecomManager == null || TextUtils.isEmpty(dialString)) { + return false; + } + if (CompatUtils.isMarshmallowCompatible()) { + return telecomManager.handleMmi(dialString, accountHandle); + } + + Object handleMmiResult = + CompatUtils.invokeMethod( + telecomManager, + "handleMmi", + new Class[] {PhoneAccountHandle.class, String.class}, + new Object[] {accountHandle, dialString}); + if (handleMmiResult != null) { + return (boolean) handleMmiResult; + } + + return telecomManager.handleMmi(dialString); + } + + /** + * Silences the ringer if a ringing call exists. Noop if {@link TelecomManager#silenceRinger()} is + * unavailable. + * + * @param telecomManager the TelecomManager to use to silence the ringer. + */ + public static void silenceRinger(@Nullable TelecomManager telecomManager) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "silenceRinger"))) { + telecomManager.silenceRinger(); + } + } + /** * Returns the current SIM call manager. Apps must be prepared for this method to return null, * indicating that there currently exists no registered SIM call manager. @@ -36,7 +292,9 @@ public class TelecomManagerCompat { */ @Nullable public static PhoneAccountHandle getSimCallManager(TelecomManager telecomManager) { - if (telecomManager != null) { + if (telecomManager != null + && (CompatUtils.isMarshmallowCompatible() + || CompatUtils.isMethodAvailable(TELECOM_MANAGER_CLASS, "getSimCallManager"))) { return telecomManager.getSimCallManager(); } return null; diff --git a/java/com/android/contacts/common/dialog/CallSubjectDialog.java b/java/com/android/contacts/common/dialog/CallSubjectDialog.java index 88fac0256..0e7937102 100644 --- a/java/com/android/contacts/common/dialog/CallSubjectDialog.java +++ b/java/com/android/contacts/common/dialog/CallSubjectDialog.java @@ -45,7 +45,7 @@ import android.widget.QuickContactBadge; import android.widget.TextView; import com.android.contacts.common.ContactPhotoManager; import com.android.contacts.common.R; -import com.android.contacts.common.lettertiles.LetterTileDrawable; +import com.android.contacts.common.compat.telecom.TelecomManagerCompat; import com.android.dialer.animation.AnimUtils; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallIntentBuilder; @@ -154,7 +154,11 @@ public class CallSubjectDialog extends Activity { .setCallSubject(subject) .build(); - getSystemService(TelecomManager.class).placeCall(intent.getData(), intent.getExtras()); + TelecomManagerCompat.placeCall( + CallSubjectDialog.this, + (TelecomManager) getSystemService(Context.TELECOM_SERVICE), + intent); + mSubjectHistory.add(subject); saveSubjectHistory(mSubjectHistory); finish(); @@ -198,7 +202,7 @@ public class CallSubjectDialog extends Activity { number /* number */, null /* displayNumber */, null /* numberLabel */, - LetterTileDrawable.TYPE_DEFAULT, + ContactPhotoManager.TYPE_DEFAULT, null /* phoneAccountHandle */); } @@ -352,7 +356,7 @@ public class CallSubjectDialog extends Activity { mNumber = arguments.getString(ARG_NUMBER); mDisplayNumber = arguments.getString(ARG_DISPLAY_NUMBER); mNumberLabel = arguments.getString(ARG_NUMBER_LABEL); - mContactType = arguments.getInt(ARG_CONTACT_TYPE, LetterTileDrawable.TYPE_DEFAULT); + mContactType = arguments.getInt(ARG_CONTACT_TYPE, ContactPhotoManager.TYPE_DEFAULT); mPhoneAccountHandle = arguments.getParcelable(ARG_PHONE_ACCOUNT_HANDLE); } diff --git a/java/com/android/contacts/common/format/FormatUtils.java b/java/com/android/contacts/common/format/FormatUtils.java new file mode 100644 index 000000000..727c15b83 --- /dev/null +++ b/java/com/android/contacts/common/format/FormatUtils.java @@ -0,0 +1,181 @@ +/* + * 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.common.format; + +import android.database.CharArrayBuffer; +import android.graphics.Typeface; +import android.support.annotation.VisibleForTesting; +import android.text.SpannableString; +import android.text.style.StyleSpan; +import java.util.Arrays; + +/** Assorted utility methods related to text formatting in Contacts. */ +public class FormatUtils { + + /** + * Finds the earliest point in buffer1 at which the first part of buffer2 matches. For example, + * overlapPoint("abcd", "cdef") == 2. + */ + public static int overlapPoint(CharArrayBuffer buffer1, CharArrayBuffer buffer2) { + if (buffer1 == null || buffer2 == null) { + return -1; + } + return overlapPoint( + Arrays.copyOfRange(buffer1.data, 0, buffer1.sizeCopied), + Arrays.copyOfRange(buffer2.data, 0, buffer2.sizeCopied)); + } + + /** + * Finds the earliest point in string1 at which the first part of string2 matches. For example, + * overlapPoint("abcd", "cdef") == 2. + */ + @VisibleForTesting + public static int overlapPoint(String string1, String string2) { + if (string1 == null || string2 == null) { + return -1; + } + return overlapPoint(string1.toCharArray(), string2.toCharArray()); + } + + /** + * Finds the earliest point in array1 at which the first part of array2 matches. For example, + * overlapPoint("abcd", "cdef") == 2. + */ + public static int overlapPoint(char[] array1, char[] array2) { + if (array1 == null || array2 == null) { + return -1; + } + int count1 = array1.length; + int count2 = array2.length; + + // Ignore matching tails of the two arrays. + while (count1 > 0 && count2 > 0 && array1[count1 - 1] == array2[count2 - 1]) { + count1--; + count2--; + } + + int size = count2; + for (int i = 0; i < count1; i++) { + if (i + size > count1) { + size = count1 - i; + } + int j; + for (j = 0; j < size; j++) { + if (array1[i + j] != array2[j]) { + break; + } + } + if (j == size) { + return i; + } + } + + return -1; + } + + /** + * Applies the given style to a range of the input CharSequence. + * + * @param style The style to apply (see the style constants in {@link Typeface}). + * @param input The CharSequence to style. + * @param start Starting index of the range to style (will be clamped to be a minimum of 0). + * @param end Ending index of the range to style (will be clamped to a maximum of the input + * length). + * @param flags Bitmask for configuring behavior of the span. See {@link android.text.Spanned}. + * @return The styled CharSequence. + */ + public static CharSequence applyStyleToSpan( + int style, CharSequence input, int start, int end, int flags) { + // Enforce bounds of the char sequence. + start = Math.max(0, start); + end = Math.min(input.length(), end); + SpannableString text = new SpannableString(input); + text.setSpan(new StyleSpan(style), start, end, flags); + return text; + } + + @VisibleForTesting + public static void copyToCharArrayBuffer(String text, CharArrayBuffer buffer) { + if (text != null) { + char[] data = buffer.data; + if (data == null || data.length < text.length()) { + buffer.data = text.toCharArray(); + } else { + text.getChars(0, text.length(), data, 0); + } + buffer.sizeCopied = text.length(); + } else { + buffer.sizeCopied = 0; + } + } + + /** Returns a String that represents the content of the given {@link CharArrayBuffer}. */ + @VisibleForTesting + public static String charArrayBufferToString(CharArrayBuffer buffer) { + return new String(buffer.data, 0, buffer.sizeCopied); + } + + /** + * Finds the index of the first word that starts with the given prefix. + * + *

If not found, returns -1. + * + * @param text the text in which to search for the prefix + * @param prefix the text to find, in upper case letters + */ + public static int indexOfWordPrefix(CharSequence text, String prefix) { + if (prefix == null || text == null) { + return -1; + } + + int textLength = text.length(); + int prefixLength = prefix.length(); + + if (prefixLength == 0 || textLength < prefixLength) { + return -1; + } + + int i = 0; + while (i < textLength) { + // Skip non-word characters + while (i < textLength && !Character.isLetterOrDigit(text.charAt(i))) { + i++; + } + + if (i + prefixLength > textLength) { + return -1; + } + + // Compare the prefixes + int j; + for (j = 0; j < prefixLength; j++) { + if (Character.toUpperCase(text.charAt(i + j)) != prefix.charAt(j)) { + break; + } + } + if (j == prefixLength) { + return i; + } + + // Skip this word + while (i < textLength && Character.isLetterOrDigit(text.charAt(i))) { + i++; + } + } + + return -1; + } +} diff --git a/java/com/android/contacts/common/format/TextHighlighter.java b/java/com/android/contacts/common/format/TextHighlighter.java index f397f0429..30c03fdf3 100644 --- a/java/com/android/contacts/common/format/TextHighlighter.java +++ b/java/com/android/contacts/common/format/TextHighlighter.java @@ -24,6 +24,8 @@ import android.widget.TextView; /** Highlights the text in a text field. */ public class TextHighlighter { + private static final boolean DEBUG = false; + private final String TAG = TextHighlighter.class.getSimpleName(); private int mTextStyle; private CharacterStyle mTextStyleSpan; @@ -79,7 +81,7 @@ public class TextHighlighter { } final String trimmedPrefix = prefix.substring(prefixStart); - int index = indexOfWordPrefix(text, trimmedPrefix); + int index = FormatUtils.indexOfWordPrefix(text, trimmedPrefix); if (index != -1) { final SpannableString result = new SpannableString(text); result.setSpan(mTextStyleSpan, index, index + trimmedPrefix.length(), 0 /* flags */); @@ -88,55 +90,4 @@ public class TextHighlighter { return text; } } - - /** - * Finds the index of the first word that starts with the given prefix. - * - *

If not found, returns -1. - * - * @param text the text in which to search for the prefix - * @param prefix the text to find, in upper case letters - */ - public static int indexOfWordPrefix(CharSequence text, String prefix) { - if (prefix == null || text == null) { - return -1; - } - - int textLength = text.length(); - int prefixLength = prefix.length(); - - if (prefixLength == 0 || textLength < prefixLength) { - return -1; - } - - int i = 0; - while (i < textLength) { - // Skip non-word characters - while (i < textLength && !Character.isLetterOrDigit(text.charAt(i))) { - i++; - } - - if (i + prefixLength > textLength) { - return -1; - } - - // Compare the prefixes - int j; - for (j = 0; j < prefixLength; j++) { - if (Character.toUpperCase(text.charAt(i + j)) != prefix.charAt(j)) { - break; - } - } - if (j == prefixLength) { - return i; - } - - // Skip this word - while (i < textLength && Character.isLetterOrDigit(text.charAt(i))) { - i++; - } - } - - return -1; - } } diff --git a/java/com/android/contacts/common/format/testing/SpannedTestUtils.java b/java/com/android/contacts/common/format/testing/SpannedTestUtils.java new file mode 100644 index 000000000..293d9d5ad --- /dev/null +++ b/java/com/android/contacts/common/format/testing/SpannedTestUtils.java @@ -0,0 +1,85 @@ +/* + * 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.common.format.testing; + +import android.test.suitebuilder.annotation.SmallTest; +import android.text.Html; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.TextUtils; +import android.text.style.StyleSpan; +import android.widget.TextView; +import junit.framework.Assert; + +/** Utility class to check the value of spanned text in text views. */ +@SmallTest +public class SpannedTestUtils { + + /** + * Checks that the text contained in the text view matches the given HTML text. + * + * @param expectedHtmlText the expected text to be in the text view + * @param textView the text view from which to get the text + */ + public static void checkHtmlText(String expectedHtmlText, TextView textView) { + String actualHtmlText = Html.toHtml((Spanned) textView.getText()); + if (TextUtils.isEmpty(expectedHtmlText)) { + // If the text is empty, it does not add the

bits to it. + Assert.assertEquals("", actualHtmlText); + } else { + Assert.assertEquals("

" + expectedHtmlText + "

\n", actualHtmlText); + } + } + + /** + * Assert span exists in the correct location. + * + * @param seq The spannable string to check. + * @param start The starting index. + * @param end The ending index. + */ + public static void assertPrefixSpan(CharSequence seq, int start, int end) { + Assert.assertTrue(seq instanceof Spanned); + Spanned spannable = (Spanned) seq; + + if (start > 0) { + Assert.assertEquals(0, getNumForegroundColorSpansBetween(spannable, 0, start - 1)); + } + Assert.assertEquals(1, getNumForegroundColorSpansBetween(spannable, start, end)); + Assert.assertEquals( + 0, getNumForegroundColorSpansBetween(spannable, end + 1, spannable.length() - 1)); + } + + private static int getNumForegroundColorSpansBetween(Spanned value, int start, int end) { + return value.getSpans(start, end, StyleSpan.class).length; + } + + /** + * Asserts that the given character sequence is not a Spanned object and text is correct. + * + * @param seq The sequence to check. + * @param expected The expected text. + */ + public static void assertNotSpanned(CharSequence seq, String expected) { + Assert.assertFalse(seq instanceof Spanned); + Assert.assertEquals(expected, seq); + } + + public static int getNextTransition(SpannableString seq, int start) { + return seq.nextSpanTransition(start, seq.length(), StyleSpan.class); + } +} diff --git a/java/com/android/contacts/common/lettertiles/LetterTileDrawable.java b/java/com/android/contacts/common/lettertiles/LetterTileDrawable.java index 5e77b696b..88522c44b 100644 --- a/java/com/android/contacts/common/lettertiles/LetterTileDrawable.java +++ b/java/com/android/contacts/common/lettertiles/LetterTileDrawable.java @@ -30,10 +30,8 @@ import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.support.annotation.IntDef; import android.support.annotation.Nullable; -import android.telecom.TelecomManager; import android.text.TextUtils; import com.android.contacts.common.R; -import com.android.contacts.common.lettertiles.LetterTileDrawable.ContactType; import com.android.dialer.common.Assert; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -408,30 +406,4 @@ public class LetterTileDrawable extends Drawable { } return this; } - - /** - * Returns the appropriate LetterTileDrawable.TYPE_ based on the given primitive conditions. - * - *

If no special state is detected, yields TYPE_DEFAULT - */ - public static @ContactType int getContactTypeFromPrimitives( - boolean isVoicemailNumber, - boolean isSpam, - boolean isBusiness, - int numberPresentation, - boolean isConference) { - if (isVoicemailNumber) { - return LetterTileDrawable.TYPE_VOICEMAIL; - } else if (isSpam) { - return LetterTileDrawable.TYPE_SPAM; - } else if (isBusiness) { - return LetterTileDrawable.TYPE_BUSINESS; - } else if (numberPresentation == TelecomManager.PRESENTATION_RESTRICTED) { - return LetterTileDrawable.TYPE_GENERIC_AVATAR; - } else if (isConference) { - return LetterTileDrawable.TYPE_CONFERENCE; - } else { - return LetterTileDrawable.TYPE_DEFAULT; - } - } } diff --git a/java/com/android/contacts/common/list/ContactEntryListAdapter.java b/java/com/android/contacts/common/list/ContactEntryListAdapter.java index 064214ef2..18bbae382 100644 --- a/java/com/android/contacts/common/list/ContactEntryListAdapter.java +++ b/java/com/android/contacts/common/list/ContactEntryListAdapter.java @@ -26,6 +26,7 @@ import android.provider.ContactsContract.CommonDataKinds.Phone; import android.provider.ContactsContract.Contacts; import android.provider.ContactsContract.Directory; import android.text.TextUtils; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -38,10 +39,7 @@ import com.android.contacts.common.ContactsUtils; import com.android.contacts.common.R; import com.android.contacts.common.compat.DirectoryCompat; import com.android.contacts.common.util.SearchUtil; -import com.android.dialer.common.LogUtil; import com.android.dialer.compat.CompatUtils; -import com.android.dialer.logging.InteractionEvent; -import com.android.dialer.logging.Logger; import java.util.HashSet; /** @@ -356,7 +354,7 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { if (cursor.getCount() == 0) { // Directory table must have at least local directory, without which this adapter will // enter very weird state. - LogUtil.e( + Log.e( TAG, "Directory search loader returned an empty cursor, which implies we have " + "no directory entries.", @@ -680,9 +678,6 @@ public abstract class ContactEntryListAdapter extends IndexerListAdapter { // mimetype here is reasonable. quickContact.setPrioritizedMimeType(Phone.CONTENT_ITEM_TYPE); } - Logger.get(mContext) - .logQuickContactOnTouch( - quickContact, InteractionEvent.Type.OPEN_QUICK_CONTACT_FROM_SEARCH, true); if (photoId != 0 || photoUriColumn == -1) { getPhotoLoader().loadThumbnail(quickContact, photoId, mDarkTheme, mCircularPhotos, null); diff --git a/java/com/android/contacts/common/list/ContactEntryListFragment.java b/java/com/android/contacts/common/list/ContactEntryListFragment.java index 146986f75..278175c0b 100644 --- a/java/com/android/contacts/common/list/ContactEntryListFragment.java +++ b/java/com/android/contacts/common/list/ContactEntryListFragment.java @@ -555,6 +555,7 @@ public abstract class ContactEntryListFragment(); + mNumberHighlightSequence = new ArrayList<>(); + } + + public ContactListItemView( + Context context, + AttributeSet attrs, + boolean supportVideoCallIcon, + boolean supportCallAndShare) { this(context, attrs); mSupportVideoCall = supportVideoCallIcon; + mSupportCallAndShare = supportCallAndShare; } public ContactListItemView(Context context, AttributeSet attrs) { @@ -273,20 +284,19 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj mNameHighlightSequence = new ArrayList<>(); mNumberHighlightSequence = new ArrayList<>(); - mCallToActionView = new ImageView(getContext()); - mCallToActionView.setId(R.id.call_to_action); - mCallToActionView.setLayoutParams(new LayoutParams(mCallToActionSize, mCallToActionSize)); - mCallToActionView.setScaleType(ScaleType.CENTER); - mCallToActionView.setImageTintList( - ContextCompat.getColorStateList(getContext(), R.color.search_video_call_icon_tint)); - addView(mCallToActionView); - setLayoutDirection(View.LAYOUT_DIRECTION_LOCALE); } - public static PhotoPosition getDefaultPhotoPosition() { - int layoutDirection = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()); - return layoutDirection == View.LAYOUT_DIRECTION_RTL ? PhotoPosition.RIGHT : PhotoPosition.LEFT; + public static PhotoPosition getDefaultPhotoPosition(boolean opposite) { + final Locale locale = Locale.getDefault(); + final int layoutDirection = TextUtils.getLayoutDirectionFromLocale(locale); + switch (layoutDirection) { + case View.LAYOUT_DIRECTION_RTL: + return (opposite ? PhotoPosition.LEFT : PhotoPosition.RIGHT); + case View.LAYOUT_DIRECTION_LTR: + default: + return (opposite ? PhotoPosition.RIGHT : PhotoPosition.LEFT); + } } /** @@ -323,47 +333,35 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj * @param position The position in the adapter of the call to action. */ public void setCallToAction(@CallToAction int action, Listener listener, int position) { - mCallToAction = action; - mPosition = position; - - Drawable drawable; + int drawable; int description; OnClickListener onClickListener; - if (action == CALL_AND_SHARE) { - drawable = getContext().getResources().getDrawable(R.drawable.ic_phone_attach); - drawable.setAutoMirrored(true); - description = R.string.description_search_call_and_share; + if (action == CALL_AND_SHARE && mSupportCallAndShare) { + drawable = R.drawable.ic_call_and_share; + description = R.string.description_search_video_call; onClickListener = v -> listener.onCallAndShareIconClicked(position); } else if (action == VIDEO && mSupportVideoCall) { - drawable = - getContext().getResources().getDrawable(R.drawable.quantum_ic_videocam_vd_theme_24); - drawable.setAutoMirrored(true); - description = R.string.description_search_video_call; + drawable = R.drawable.ic_search_video_call; + description = R.string.description_search_call_and_share; onClickListener = v -> listener.onVideoCallIconClicked(position); - } else if (action == LIGHTBRINGER) { - drawable = - getContext().getResources().getDrawable(R.drawable.quantum_ic_videocam_vd_theme_24); - drawable.setAutoMirrored(true); - description = R.string.description_search_video_call; - onClickListener = v -> listener.onLightbringerIconClicked(position); } else { - mCallToActionView.setVisibility(View.GONE); - mCallToActionView.setOnClickListener(null); + if (mCallToAction != null) { + mCallToAction.setVisibility(View.GONE); + mCallToAction.setOnClickListener(null); + } return; } - mCallToActionView.setContentDescription(getContext().getString(description)); - mCallToActionView.setOnClickListener(onClickListener); - mCallToActionView.setImageDrawable(drawable); - mCallToActionView.setVisibility(View.VISIBLE); - } - - public @CallToAction int getCallToAction() { - return mCallToAction; - } - - public int getPosition() { - return mPosition; + if (mCallToAction == null) { + mCallToAction = new ImageView(getContext()); + mCallToAction.setLayoutParams(new LayoutParams(mCallToActionSize, mCallToActionSize)); + mCallToAction.setScaleType(ScaleType.CENTER); + addView(mCallToAction); + } + mCallToAction.setContentDescription(getContext().getString(description)); + mCallToAction.setOnClickListener(onClickListener); + mCallToAction.setImageResource(drawable); + mCallToAction.setVisibility(View.VISIBLE); } /** @@ -378,6 +376,18 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj mSupportVideoCall = supportVideoCall; } + /** + * Sets whether the view supports a call and share icon. This is independent of whether the view + * is actually showing an icon. Support for the icon ensures that the layout leaves space for it, + * should it be shown. + * + * @param supportCallAndShare {@code true} if the call and share icon is supported, {@code false} + * otherwise. + */ + public void setSupportCallAndShareIcon(boolean supportCallAndShare) { + mSupportCallAndShare = supportCallAndShare; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // We will match parent's width and wrap content vertically, but make sure @@ -413,7 +423,9 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj effectiveWidth -= mHeaderWidth + mGapBetweenImageAndText; } - effectiveWidth -= (mCallToActionSize + mCallToActionMargin); + if (mSupportVideoCall || mSupportCallAndShare) { + effectiveWidth -= (mCallToActionSize + mCallToActionMargin); + } // Go over all visible text views and measure actual width of each of them. // Also calculate their heights to get the total height for this entire view. @@ -484,9 +496,11 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj mStatusTextViewHeight = mPresenceIcon.getMeasuredHeight(); } - mCallToActionView.measure( - MeasureSpec.makeMeasureSpec(mCallToActionSize, MeasureSpec.EXACTLY), - MeasureSpec.makeMeasureSpec(mCallToActionSize, MeasureSpec.EXACTLY)); + if ((mSupportVideoCall || mSupportCallAndShare) && isVisible(mCallToAction)) { + mCallToAction.measure( + MeasureSpec.makeMeasureSpec(mCallToActionSize, MeasureSpec.EXACTLY), + MeasureSpec.makeMeasureSpec(mCallToActionSize, MeasureSpec.EXACTLY)); + } if (isVisible(mWorkProfileIcon)) { mWorkProfileIcon.measure( @@ -615,30 +629,34 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj leftBound += mTextIndent; } - // Place the call to action at the end of the list (e.g. take into account RTL mode). - // Center the icon vertically - final int callToActionTop = topBound + (height - topBound - mCallToActionSize) / 2; + if (mSupportVideoCall || mSupportCallAndShare) { + // Place the call to action at the end of the list (e.g. take into account RTL mode). + if (isVisible(mCallToAction)) { + // Center the icon vertically + final int callToActionTop = topBound + (height - topBound - mCallToActionSize) / 2; - if (!isLayoutRtl) { - // When photo is on left, icon is placed on the right edge. - mCallToActionView.layout( - rightBound - mCallToActionSize, - callToActionTop, - rightBound, - callToActionTop + mCallToActionSize); - } else { - // When photo is on right, icon is placed on the left edge. - mCallToActionView.layout( - leftBound, - callToActionTop, - leftBound + mCallToActionSize, - callToActionTop + mCallToActionSize); - } + if (!isLayoutRtl) { + // When photo is on left, icon is placed on the right edge. + mCallToAction.layout( + rightBound - mCallToActionSize, + callToActionTop, + rightBound, + callToActionTop + mCallToActionSize); + } else { + // When photo is on right, icon is placed on the left edge. + mCallToAction.layout( + leftBound, + callToActionTop, + leftBound + mCallToActionSize, + callToActionTop + mCallToActionSize); + } + } - if (mPhotoPosition == PhotoPosition.LEFT) { - rightBound -= (mCallToActionSize + mCallToActionMargin); - } else { - leftBound += mCallToActionSize + mCallToActionMargin; + if (mPhotoPosition == PhotoPosition.LEFT) { + rightBound -= (mCallToActionSize + mCallToActionMargin); + } else { + leftBound += mCallToActionSize + mCallToActionMargin; + } } // Center text vertically, then apply the top offset. @@ -883,7 +901,9 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj } if (mQuickContact == null) { mQuickContact = new QuickContactBadge(getContext()); - mQuickContact.setOverlay(null); + if (CompatUtils.isLollipopCompatible()) { + mQuickContact.setOverlay(null); + } mQuickContact.setLayoutParams(getDefaultPhotoLayoutParams()); if (mNameTextView != null) { mQuickContact.setContentDescription( @@ -988,7 +1008,9 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj mNameTextView.setGravity(Gravity.CENTER_VERTICAL); mNameTextView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); mNameTextView.setId(R.id.cliv_name_textview); - mNameTextView.setElegantTextHeight(false); + if (CompatUtils.isLollipopCompatible()) { + mNameTextView.setElegantTextHeight(false); + } addView(mNameTextView); } return mNameTextView; @@ -1034,7 +1056,6 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj * exists. */ public void setPhoneNumber(String text) { - mPhoneNumber = text; if (text == null) { if (mDataView != null) { mDataView.setVisibility(View.GONE); @@ -1065,10 +1086,6 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj } } - public String getPhoneNumber() { - return mPhoneNumber; - } - private void setMarqueeText(TextView textView, CharSequence text) { if (getTextEllipsis() == TruncateAt.MARQUEE) { // To show MARQUEE correctly (with END effect during non-active state), we need @@ -1092,7 +1109,9 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj mDataView.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); mDataView.setActivated(isActivated()); mDataView.setId(R.id.cliv_data_view); - mDataView.setElegantTextHeight(false); + if (CompatUtils.isLollipopCompatible()) { + mDataView.setElegantTextHeight(false); + } addView(mDataView); } return mDataView; @@ -1450,17 +1469,28 @@ public class ContactListItemView extends ViewGroup implements SelectionBoundsAdj forceLayout(); } + public void setPhotoPosition(PhotoPosition photoPosition) { + mPhotoPosition = photoPosition; + } + /** * Set drawable resources directly for the drawable resource of the photo view. * - * @param drawable A drawable resource. + * @param drawableId Id of drawable resource. */ - public void setDrawable(Drawable drawable) { + public void setDrawableResource(int drawableId) { ImageView photo = getPhotoView(); photo.setScaleType(ImageView.ScaleType.CENTER); - int iconColor = ContextCompat.getColor(getContext(), R.color.search_shortcut_icon_color); - photo.setImageDrawable(drawable); - photo.setImageTintList(ColorStateList.valueOf(iconColor)); + final Drawable drawable = ContextCompat.getDrawable(getContext(), drawableId); + final int iconColor = ContextCompat.getColor(getContext(), R.color.search_shortcut_icon_color); + if (CompatUtils.isLollipopCompatible()) { + photo.setImageDrawable(drawable); + photo.setImageTintList(ColorStateList.valueOf(iconColor)); + } else { + final Drawable drawableWrapper = DrawableCompat.wrap(drawable).mutate(); + DrawableCompat.setTint(drawableWrapper, iconColor); + photo.setImageDrawable(drawableWrapper); + } } @Override diff --git a/java/com/android/contacts/common/list/ContactTileView.java b/java/com/android/contacts/common/list/ContactTileView.java index 15582d684..9273b0583 100644 --- a/java/com/android/contacts/common/list/ContactTileView.java +++ b/java/com/android/contacts/common/list/ContactTileView.java @@ -19,6 +19,7 @@ import android.content.Context; import android.graphics.Rect; import android.net.Uri; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; @@ -27,9 +28,6 @@ import com.android.contacts.common.ContactPhotoManager; import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; import com.android.contacts.common.MoreContactUtils; import com.android.contacts.common.R; -import com.android.dialer.callintent.CallInitiationType; -import com.android.dialer.callintent.CallSpecificAppData; -import com.android.dialer.common.LogUtil; /** A ContactTile displays a contact's picture and name */ public abstract class ContactTileView extends FrameLayout { @@ -62,14 +60,8 @@ public abstract class ContactTileView extends FrameLayout { if (mListener == null) { return; } - CallSpecificAppData callSpecificAppData = - CallSpecificAppData.newBuilder() - .setCallInitiationType(CallInitiationType.Type.SPEED_DIAL) - .build(); mListener.onContactSelected( - getLookupUri(), - MoreContactUtils.getTargetRectFromView(ContactTileView.this), - callSpecificAppData); + getLookupUri(), MoreContactUtils.getTargetRectFromView(ContactTileView.this)); } }; } @@ -105,7 +97,7 @@ public abstract class ContactTileView extends FrameLayout { } } else { - LogUtil.w(TAG, "contactPhotoManager not set"); + Log.w(TAG, "contactPhotoManager not set"); } } else { setVisibility(View.INVISIBLE); @@ -171,10 +163,9 @@ public abstract class ContactTileView extends FrameLayout { public interface Listener { /** Notification that the contact was selected; no specific action is dictated. */ - void onContactSelected( - Uri contactLookupUri, Rect viewRect, CallSpecificAppData callSpecificAppData); + void onContactSelected(Uri contactLookupUri, Rect viewRect); /** Notification that the specified number is to be called. */ - void onCallNumberDirectly(String phoneNumber, CallSpecificAppData callSpecificAppData); + void onCallNumberDirectly(String phoneNumber); } } diff --git a/java/com/android/contacts/common/list/PhoneNumberListAdapter.java b/java/com/android/contacts/common/list/PhoneNumberListAdapter.java index 65e6f2da2..9a490d78a 100644 --- a/java/com/android/contacts/common/list/PhoneNumberListAdapter.java +++ b/java/com/android/contacts/common/list/PhoneNumberListAdapter.java @@ -30,6 +30,7 @@ import android.provider.ContactsContract.Directory; import android.text.TextUtils; import android.view.View; import android.view.ViewGroup; +import com.android.contacts.common.ContactPhotoManager; import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest; import com.android.contacts.common.ContactsUtils; import com.android.contacts.common.R; @@ -37,17 +38,15 @@ import com.android.contacts.common.compat.CallableCompat; import com.android.contacts.common.compat.DirectoryCompat; import com.android.contacts.common.compat.PhoneCompat; import com.android.contacts.common.extensions.PhoneDirectoryExtenderAccessor; -import com.android.contacts.common.lettertiles.LetterTileDrawable; import com.android.contacts.common.list.ContactListItemView.CallToAction; import com.android.contacts.common.preference.ContactsPreferences; import com.android.contacts.common.util.Constants; +import com.android.dialer.callcomposer.CallComposerContact; import com.android.dialer.common.LogUtil; import com.android.dialer.compat.CompatUtils; -import com.android.dialer.dialercontact.DialerContact; import com.android.dialer.enrichedcall.EnrichedCallCapabilities; import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.enrichedcall.EnrichedCallManager; -import com.android.dialer.lightbringer.LightbringerComponent; import com.android.dialer.location.GeoUtil; import com.android.dialer.util.CallUtil; import java.util.ArrayList; @@ -69,15 +68,16 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { // A list of extended directories to add to the directories from the database private final List mExtendedDirectories; private final CharSequence mUnknownNameText; - private final boolean mIsPresenceEnabled; - protected final boolean mIsImsVideoEnabled; - + private final boolean mCallAndShareEnabled; // Extended directories will have ID's that are higher than any of the id's from the database, // so that we can identify them and set them up properly. If no extended directories // exist, this will be Long.MAX_VALUE private long mFirstExtendedDirectoryId = Long.MAX_VALUE; + private ContactListItemView.PhotoPosition mPhotoPosition; private boolean mUseCallableUri; private Listener mListener; + private boolean mIsVideoEnabled; + private boolean mIsPresenceEnabled; public PhoneNumberListAdapter(Context context) { super(context); @@ -88,8 +88,11 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { PhoneDirectoryExtenderAccessor.get(mContext).getExtendedDirectories(mContext); int videoCapabilities = CallUtil.getVideoCallingAvailability(context); - mIsImsVideoEnabled = CallUtil.isVideoEnabled(context); + mIsVideoEnabled = (videoCapabilities & CallUtil.VIDEO_CALLING_ENABLED) != 0; mIsPresenceEnabled = (videoCapabilities & CallUtil.VIDEO_CALLING_PRESENCE) != 0; + + // TODO + mCallAndShareEnabled = true; } @Override @@ -246,10 +249,10 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { return item != null ? item.getString(PhoneQuery.LOOKUP_KEY) : null; } - public DialerContact getDialerContact(int position) { + public CallComposerContact getCallComposerContact(int position) { Cursor cursor = (Cursor) getItem(position); if (cursor == null) { - LogUtil.e("PhoneNumberListAdapter.getDialerContact", "cursor was null."); + LogUtil.e("PhoneNumberListAdapter.getCallComposerContact", "cursor was null."); return null; } @@ -260,11 +263,11 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { Contacts.getLookupUri( cursor.getLong(PhoneQuery.CONTACT_ID), cursor.getString(PhoneQuery.LOOKUP_KEY)); - DialerContact.Builder contact = DialerContact.newBuilder(); + CallComposerContact.Builder contact = CallComposerContact.newBuilder(); contact .setNumber(number) .setPhotoId(cursor.getLong(PhoneQuery.PHOTO_ID)) - .setContactType(LetterTileDrawable.TYPE_DEFAULT) + .setContactType(ContactPhotoManager.TYPE_DEFAULT) .setNameOrNumber(displayName) .setNumberLabel( Phone.getTypeLabel( @@ -294,6 +297,7 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { ContactListItemView view = super.newView(context, partition, cursor, position, parent); view.setUnknownNameText(mUnknownNameText); view.setQuickContactEnabled(isQuickContactEnabled()); + view.setPhotoPosition(mPhotoPosition); return view; } @@ -397,17 +401,13 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { int carrierPresence = cursor.getInt(PhoneQuery.CARRIER_PRESENCE); boolean isPresent = (carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) != 0; - boolean isVideoIconShown = mIsImsVideoEnabled && (!mIsPresenceEnabled || isPresent); + boolean isVideoIconShown = mIsVideoEnabled && (!mIsPresenceEnabled || isPresent); if (isVideoIconShown) { action = ContactListItemView.VIDEO; } } - if (LightbringerComponent.get(mContext).getLightbringer().isReachable(mContext, number)) { - action = ContactListItemView.LIGHTBRINGER; - } - - if (action == ContactListItemView.NONE) { + if (isCallAndShareEnabled() && action == ContactListItemView.NONE && number != null) { EnrichedCallManager manager = EnrichedCallComponent.get(mContext).getEnrichedCallManager(); EnrichedCallCapabilities capabilities = manager.getCapabilities(number); if (capabilities != null && capabilities.supportsCallComposer()) { @@ -481,6 +481,14 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { } } + public ContactListItemView.PhotoPosition getPhotoPosition() { + return mPhotoPosition; + } + + public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) { + mPhotoPosition = photoPosition; + } + public void setUseCallableUri(boolean useCallableUri) { mUseCallableUri = useCallableUri; } @@ -562,12 +570,14 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { mListener = listener; } + public boolean isCallAndShareEnabled() { + return mCallAndShareEnabled; + } + public interface Listener { void onVideoCallIconClicked(int position); - void onLightbringerIconClicked(int position); - void onCallAndShareIconClicked(int position); } @@ -632,14 +642,18 @@ public class PhoneNumberListAdapter extends ContactEntryListAdapter { static { final List projectionList = new ArrayList<>(Arrays.asList(PROJECTION_PRIMARY_INTERNAL)); - projectionList.add(Phone.CARRIER_PRESENCE); // 9 + if (CompatUtils.isMarshmallowCompatible()) { + projectionList.add(Phone.CARRIER_PRESENCE); // 9 + } PROJECTION_PRIMARY = projectionList.toArray(new String[projectionList.size()]); } static { final List projectionList = new ArrayList<>(Arrays.asList(PROJECTION_ALTERNATIVE_INTERNAL)); - projectionList.add(Phone.CARRIER_PRESENCE); // 9 + if (CompatUtils.isMarshmallowCompatible()) { + projectionList.add(Phone.CARRIER_PRESENCE); // 9 + } PROJECTION_ALTERNATIVE = projectionList.toArray(new String[projectionList.size()]); } } diff --git a/java/com/android/contacts/common/list/PhoneNumberPickerFragment.java b/java/com/android/contacts/common/list/PhoneNumberPickerFragment.java index 79e670010..8f25f82a5 100644 --- a/java/com/android/contacts/common/list/PhoneNumberPickerFragment.java +++ b/java/com/android/contacts/common/list/PhoneNumberPickerFragment.java @@ -29,18 +29,15 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import com.android.contacts.common.R; -import com.android.contacts.common.list.PhoneNumberListAdapter.Listener; import com.android.contacts.common.util.AccountFilterUtil; +import com.android.dialer.callcomposer.CallComposerContact; import com.android.dialer.callintent.CallInitiationType; import com.android.dialer.callintent.CallInitiationType.Type; import com.android.dialer.callintent.CallSpecificAppData; import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; -import com.android.dialer.dialercontact.DialerContact; import com.android.dialer.enrichedcall.EnrichedCallComponent; import com.android.dialer.enrichedcall.EnrichedCallManager; -import com.android.dialer.lightbringer.LightbringerComponent; -import com.android.dialer.logging.DialerImpression; import com.android.dialer.logging.Logger; import com.android.dialer.protos.ProtoParsers; import java.util.Set; @@ -64,6 +61,9 @@ public class PhoneNumberPickerFragment extends ContactEntryListFragment mLoadFinishedListeners = new ArraySet<>(); private CursorReranker mCursorReranker; @@ -71,7 +71,7 @@ public class PhoneNumberPickerFragment extends ContactEntryListFragment in the future private static final int[] ATTRS = @@ -59,6 +54,20 @@ public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnP android.R.attr.textAllCaps }; + static { + if (CompatUtils.isLollipopCompatible()) { + VIEW_BOUNDS_OUTLINE_PROVIDER = + new ViewOutlineProvider() { + @Override + public void getOutline(View view, Outline outline) { + outline.setRect(0, 0, view.getWidth(), view.getHeight()); + } + }; + } else { + VIEW_BOUNDS_OUTLINE_PROVIDER = null; + } + } + /** * Linearlayout that will contain the TextViews serving as tabs. This is the only child of the * parent HorizontalScrollView. @@ -102,8 +111,10 @@ public class ViewPagerTabs extends HorizontalScrollView implements ViewPager.OnP new FrameLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT)); a.recycle(); - // enable shadow casting from view bounds - setOutlineProvider(VIEW_BOUNDS_OUTLINE_PROVIDER); + if (CompatUtils.isLollipopCompatible()) { + // enable shadow casting from view bounds + setOutlineProvider(VIEW_BOUNDS_OUTLINE_PROVIDER); + } } public void setViewPager(ViewPager viewPager) { diff --git a/java/com/android/contacts/common/location/CountryDetector.java b/java/com/android/contacts/common/location/CountryDetector.java new file mode 100644 index 000000000..7d9e42b38 --- /dev/null +++ b/java/com/android/contacts/common/location/CountryDetector.java @@ -0,0 +1,221 @@ +/* + * Copyright (C) 2016 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.common.location; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.location.Geocoder; +import android.location.Location; +import android.location.LocationManager; +import android.preference.PreferenceManager; +import android.telephony.TelephonyManager; +import android.text.TextUtils; +import android.util.Log; +import com.android.dialer.util.PermissionsUtil; +import java.util.Locale; + +/** + * This class is used to detect the country where the user is. It is a simplified version of the + * country detector service in the framework. The sources of country location are queried in the + * following order of reliability: + * + *

    + *
  • Mobile network + *
  • Location manager + *
  • SIM's country + *
  • User's default locale + *
+ * + * As far as possible this class tries to replicate the behavior of the system's country detector + * service: 1) Order in priority of sources of country location 2) Mobile network information + * provided by CDMA phones is ignored 3) Location information is updated every 12 hours (instead of + * 24 hours in the system) 4) Location updates only uses the {@link + * LocationManager#PASSIVE_PROVIDER} to avoid active use of the GPS 5) If a location is successfully + * obtained and geocoded, we never fall back to use of the SIM's country (for the system, the + * fallback never happens without a reboot) 6) Location is not used if the device does not implement + * a {@link android.location.Geocoder} + */ +public class CountryDetector { + + public static final String KEY_PREFERENCE_TIME_UPDATED = "preference_time_updated"; + public static final String KEY_PREFERENCE_CURRENT_COUNTRY = "preference_current_country"; + private static final String TAG = "CountryDetector"; + // Wait 12 hours between updates + private static final long TIME_BETWEEN_UPDATES_MS = 1000L * 60 * 60 * 12; + // Minimum distance before an update is triggered, in meters. We don't need this to be too + // exact because all we care about is what country the user is in. + private static final long DISTANCE_BETWEEN_UPDATES_METERS = 5000; + private static CountryDetector sInstance; + private final TelephonyManager mTelephonyManager; + private final LocationManager mLocationManager; + private final LocaleProvider mLocaleProvider; + // Used as a default country code when all the sources of country data have failed in the + // exceedingly rare event that the device does not have a default locale set for some reason. + private static final String DEFAULT_COUNTRY_ISO = "US"; + private final Context mContext; + + private CountryDetector(Context context) { + this( + context, + (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE), + (LocationManager) context.getSystemService(Context.LOCATION_SERVICE), + new LocaleProvider()); + } + + private CountryDetector( + Context context, + TelephonyManager telephonyManager, + LocationManager locationManager, + LocaleProvider localeProvider) { + mTelephonyManager = telephonyManager; + mLocationManager = locationManager; + mLocaleProvider = localeProvider; + mContext = context; + + registerForLocationUpdates(context, mLocationManager); + } + + public static void registerForLocationUpdates(Context context, LocationManager locationManager) { + if (!PermissionsUtil.hasLocationPermissions(context)) { + Log.w(TAG, "No location permissions, not registering for location updates."); + return; + } + + if (!Geocoder.isPresent()) { + // Certain devices do not have an implementation of a geocoder - in that case there is + // no point trying to get location updates because we cannot retrieve the country based + // on the location anyway. + return; + } + final Intent activeIntent = new Intent(context, LocationChangedReceiver.class); + final PendingIntent pendingIntent = + PendingIntent.getBroadcast(context, 0, activeIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + locationManager.requestLocationUpdates( + LocationManager.PASSIVE_PROVIDER, + TIME_BETWEEN_UPDATES_MS, + DISTANCE_BETWEEN_UPDATES_METERS, + pendingIntent); + } + + /** + * Returns the instance of the country detector. {@link #initialize(Context)} must have been + * called previously. + * + * @return the initialized country detector. + */ + public static synchronized CountryDetector getInstance(Context context) { + if (sInstance == null) { + sInstance = new CountryDetector(context.getApplicationContext()); + } + return sInstance; + } + + /** Factory method for {@link CountryDetector} that allows the caller to provide mock objects. */ + public CountryDetector getInstanceForTest( + Context context, + TelephonyManager telephonyManager, + LocationManager locationManager, + LocaleProvider localeProvider, + Geocoder geocoder) { + return new CountryDetector(context, telephonyManager, locationManager, localeProvider); + } + + public String getCurrentCountryIso() { + String result = null; + if (isNetworkCountryCodeAvailable()) { + result = getNetworkBasedCountryIso(); + } + if (TextUtils.isEmpty(result)) { + result = getLocationBasedCountryIso(); + } + if (TextUtils.isEmpty(result)) { + result = getSimBasedCountryIso(); + } + if (TextUtils.isEmpty(result)) { + result = getLocaleBasedCountryIso(); + } + if (TextUtils.isEmpty(result)) { + result = DEFAULT_COUNTRY_ISO; + } + return result.toUpperCase(Locale.US); + } + + /** @return the country code of the current telephony network the user is connected to. */ + private String getNetworkBasedCountryIso() { + return mTelephonyManager.getNetworkCountryIso(); + } + + /** @return the geocoded country code detected by the {@link LocationManager}. */ + private String getLocationBasedCountryIso() { + if (!Geocoder.isPresent() || !PermissionsUtil.hasLocationPermissions(mContext)) { + return null; + } + final SharedPreferences sharedPreferences = + PreferenceManager.getDefaultSharedPreferences(mContext); + return sharedPreferences.getString(KEY_PREFERENCE_CURRENT_COUNTRY, null); + } + + /** @return the country code of the SIM card currently inserted in the device. */ + private String getSimBasedCountryIso() { + return mTelephonyManager.getSimCountryIso(); + } + + /** @return the country code of the user's currently selected locale. */ + private String getLocaleBasedCountryIso() { + Locale defaultLocale = mLocaleProvider.getDefaultLocale(); + if (defaultLocale != null) { + return defaultLocale.getCountry(); + } + return null; + } + + private boolean isNetworkCountryCodeAvailable() { + // On CDMA TelephonyManager.getNetworkCountryIso() just returns the SIM's country code. + // In this case, we want to ignore the value returned and fallback to location instead. + return mTelephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_GSM; + } + + /** + * Class that can be used to return the user's default locale. This is in its own class so that it + * can be mocked out. + */ + public static class LocaleProvider { + + public Locale getDefaultLocale() { + return Locale.getDefault(); + } + } + + public static class LocationChangedReceiver extends BroadcastReceiver { + + @Override + public void onReceive(final Context context, Intent intent) { + if (!intent.hasExtra(LocationManager.KEY_LOCATION_CHANGED)) { + return; + } + + final Location location = + (Location) intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED); + + UpdateCountryService.updateCountry(context, location); + } + } +} diff --git a/java/com/android/contacts/common/location/UpdateCountryService.java b/java/com/android/contacts/common/location/UpdateCountryService.java new file mode 100644 index 000000000..f23e09e20 --- /dev/null +++ b/java/com/android/contacts/common/location/UpdateCountryService.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2016 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.common.location; + +import android.app.IntentService; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.location.Address; +import android.location.Geocoder; +import android.location.Location; +import android.preference.PreferenceManager; +import android.util.Log; +import java.io.IOException; +import java.util.List; + +/** + * Service used to perform asynchronous geocoding from within a broadcast receiver. Given a {@link + * Location}, convert it into a country code, and save it in shared preferences. + */ +public class UpdateCountryService extends IntentService { + + private static final String TAG = UpdateCountryService.class.getSimpleName(); + + private static final String ACTION_UPDATE_COUNTRY = "saveCountry"; + + private static final String KEY_INTENT_LOCATION = "location"; + + public UpdateCountryService() { + super(TAG); + } + + public static void updateCountry(Context context, Location location) { + final Intent serviceIntent = new Intent(context, UpdateCountryService.class); + serviceIntent.setAction(ACTION_UPDATE_COUNTRY); + serviceIntent.putExtra(UpdateCountryService.KEY_INTENT_LOCATION, location); + context.startService(serviceIntent); + } + + @Override + protected void onHandleIntent(Intent intent) { + if (intent == null) { + Log.d(TAG, "onHandleIntent: could not handle null intent"); + return; + } + if (ACTION_UPDATE_COUNTRY.equals(intent.getAction())) { + final Location location = intent.getParcelableExtra(KEY_INTENT_LOCATION); + final String country = getCountryFromLocation(getApplicationContext(), location); + + if (country == null) { + return; + } + + final SharedPreferences prefs = + PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); + + final Editor editor = prefs.edit(); + editor.putLong(CountryDetector.KEY_PREFERENCE_TIME_UPDATED, System.currentTimeMillis()); + editor.putString(CountryDetector.KEY_PREFERENCE_CURRENT_COUNTRY, country); + editor.commit(); + } + } + + /** + * Given a {@link Location}, return a country code. + * + * @return the ISO 3166-1 two letter country code + */ + private String getCountryFromLocation(Context context, Location location) { + final Geocoder geocoder = new Geocoder(context); + String country = null; + try { + double latitude = location.getLatitude(); + // Latitude has to be between 90 and -90 (latitude of north and south poles wrt equator) + if (latitude <= 90 && latitude >= -90) { + final List
addresses = + geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1); + if (addresses != null && addresses.size() > 0) { + country = addresses.get(0).getCountryCode(); + } + } else { + Log.w(TAG, "Invalid latitude"); + } + } catch (IOException e) { + Log.w(TAG, "Exception occurred when getting geocoded country from location"); + } + return country; + } +} diff --git a/java/com/android/contacts/common/model/ContactLoader.java b/java/com/android/contacts/common/model/ContactLoader.java index 7ef3b73a0..55464b161 100644 --- a/java/com/android/contacts/common/model/ContactLoader.java +++ b/java/com/android/contacts/common/model/ContactLoader.java @@ -522,7 +522,6 @@ public class ContactLoader extends AsyncTaskLoader { cursorColumnToContentValues(cursor, cv, ContactQuery.TIMES_USED); cursorColumnToContentValues(cursor, cv, ContactQuery.LAST_TIME_USED); cursorColumnToContentValues(cursor, cv, ContactQuery.CARRIER_PRESENCE); - return cv; } diff --git a/java/com/android/contacts/common/model/account/BaseAccountType.java b/java/com/android/contacts/common/model/account/BaseAccountType.java index ccdad3641..8e9ba738c 100644 --- a/java/com/android/contacts/common/model/account/BaseAccountType.java +++ b/java/com/android/contacts/common/model/account/BaseAccountType.java @@ -307,7 +307,7 @@ public abstract class BaseAccountType extends AccountType { DataKind kind = addKind( new DataKind(Phone.CONTENT_ITEM_TYPE, R.string.phoneLabelsGroup, Weight.PHONE, true)); - kind.iconAltRes = R.drawable.quantum_ic_message_vd_theme_24; + kind.iconAltRes = R.drawable.quantum_ic_message_white_24; kind.iconAltDescriptionRes = R.string.sms; kind.actionHeader = new PhoneActionInflater(); kind.actionAltHeader = new PhoneActionAltInflater(); @@ -1228,7 +1228,7 @@ public abstract class BaseAccountType extends AccountType { new PhoneActionInflater(), new SimpleInflater(Phone.NUMBER)); - kind.iconAltRes = R.drawable.quantum_ic_message_vd_theme_24; + kind.iconAltRes = R.drawable.quantum_ic_message_white_24; kind.iconAltDescriptionRes = R.string.sms; kind.actionAltHeader = new PhoneActionAltInflater(); diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_ab_search.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_ab_search.png new file mode 100644 index 000000000..d86b2195a Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_ab_search.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_arrow_back_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_arrow_back_24dp.png new file mode 100644 index 000000000..ddbb2c459 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_arrow_back_24dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_business_white_120dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_business_white_120dp.png new file mode 100644 index 000000000..d5942dcad Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_business_white_120dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_call_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_call_24dp.png new file mode 100644 index 000000000..4dc506515 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_call_24dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_close_dk.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_close_dk.png new file mode 100644 index 000000000..969552935 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_close_dk.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_create_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_create_24dp.png new file mode 100644 index 000000000..540ab4dee Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_create_24dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_group_white_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_group_white_24dp.png new file mode 100644 index 000000000..017e4bbf7 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_group_white_24dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_history_white_drawable_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_history_white_drawable_24dp.png new file mode 100644 index 000000000..703d30b92 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_history_white_drawable_24dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_info_outline_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_info_outline_24dp.png new file mode 100644 index 000000000..c7b1113cf Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_info_outline_24dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_back.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_back.png new file mode 100644 index 000000000..deb3a6dc1 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_back.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_dk.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_dk.png new file mode 100644 index 000000000..06bd18fbb Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_dk.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_lt.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_lt.png new file mode 100644 index 000000000..d829d11e2 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_group_lt.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_overflow_lt.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_overflow_lt.png new file mode 100644 index 000000000..1ba12950c Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_overflow_lt.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_dk.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_dk.png new file mode 100644 index 000000000..5ff3ac574 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_dk.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_lt.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_lt.png new file mode 100644 index 000000000..b4ebfc7b2 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_person_lt.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_remove_field_holo_light.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_remove_field_holo_light.png new file mode 100644 index 000000000..03fd2fb10 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_remove_field_holo_light.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_dk.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_dk.png new file mode 100644 index 000000000..e8cb0f5fe Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_dk.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_holo_light.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_holo_light.png new file mode 100644 index 000000000..45137967c Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_holo_light.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_lt.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_lt.png new file mode 100644 index 000000000..1c9bb81fa Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_menu_star_lt.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_person_24dp.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_person_24dp.png new file mode 100644 index 000000000..56708b0ba Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_person_24dp.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_rx_videocam.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_rx_videocam.png new file mode 100644 index 000000000..ccdda6701 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_rx_videocam.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_tx_videocam.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_tx_videocam.png new file mode 100644 index 000000000..603ddc895 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_tx_videocam.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_videocam.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_videocam.png new file mode 100644 index 000000000..97905c9f5 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_videocam.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/ic_voicemail_avatar.png b/java/com/android/contacts/common/res/drawable-hdpi/ic_voicemail_avatar.png new file mode 100644 index 000000000..2121878ae Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/ic_voicemail_avatar.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-hdpi/list_activated_holo.9.png new file mode 100644 index 000000000..4ea7afa00 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/list_background_holo.9.png b/java/com/android/contacts/common/res/drawable-hdpi/list_background_holo.9.png new file mode 100644 index 000000000..cddf9be75 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/list_background_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/list_focused_holo.9.png b/java/com/android/contacts/common/res/drawable-hdpi/list_focused_holo.9.png new file mode 100644 index 000000000..86578be45 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/list_focused_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/list_longpressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-hdpi/list_longpressed_holo_light.9.png new file mode 100644 index 000000000..e9afcc924 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/list_longpressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/list_pressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-hdpi/list_pressed_holo_light.9.png new file mode 100644 index 000000000..2054530ed Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/list_pressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/list_section_divider_holo_custom.9.png b/java/com/android/contacts/common/res/drawable-hdpi/list_section_divider_holo_custom.9.png new file mode 100644 index 000000000..a0f17568e Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/list_section_divider_holo_custom.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-hdpi/list_title_holo.9.png b/java/com/android/contacts/common/res/drawable-hdpi/list_title_holo.9.png new file mode 100644 index 000000000..ae937176e Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-hdpi/list_title_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_focused_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_focused_holo.9.png new file mode 100644 index 000000000..4139942d6 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_focused_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png new file mode 100644 index 000000000..569d28f54 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_section_divider_holo_custom.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_title_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_title_holo.9.png new file mode 100644 index 000000000..5ec4c96a7 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-hdpi/list_title_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_focused_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_focused_holo.9.png new file mode 100644 index 000000000..4139942d6 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_focused_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png new file mode 100644 index 000000000..065ff62ce Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_section_divider_holo_custom.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_title_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_title_holo.9.png new file mode 100644 index 000000000..013d5e711 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-mdpi/list_title_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png new file mode 100644 index 000000000..947f03cec Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-hdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png new file mode 100644 index 000000000..6d09d7278 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-mdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png new file mode 100644 index 000000000..63c7456f0 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-sw600dp-xhdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png new file mode 100644 index 000000000..4139942d6 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_focused_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png new file mode 100644 index 000000000..af5855420 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_section_divider_holo_custom.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_title_holo.9.png b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_title_holo.9.png new file mode 100644 index 000000000..cb801ac1b Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-ldrtl-xhdpi/list_title_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-mdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-mdpi/list_activated_holo.9.png new file mode 100644 index 000000000..3bf8e0362 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-mdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-mdpi/list_focused_holo.9.png b/java/com/android/contacts/common/res/drawable-mdpi/list_focused_holo.9.png new file mode 100644 index 000000000..86578be45 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-mdpi/list_focused_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-mdpi/list_longpressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-mdpi/list_longpressed_holo_light.9.png new file mode 100644 index 000000000..3226ab760 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-mdpi/list_longpressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-mdpi/list_pressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-mdpi/list_pressed_holo_light.9.png new file mode 100644 index 000000000..061904c42 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-mdpi/list_pressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-mdpi/list_section_divider_holo_custom.9.png b/java/com/android/contacts/common/res/drawable-mdpi/list_section_divider_holo_custom.9.png new file mode 100644 index 000000000..1d9371de0 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-mdpi/list_section_divider_holo_custom.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-sw600dp-hdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-sw600dp-hdpi/list_activated_holo.9.png new file mode 100644 index 000000000..046b24a96 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-sw600dp-hdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-sw600dp-mdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-sw600dp-mdpi/list_activated_holo.9.png new file mode 100644 index 000000000..1ff337370 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-sw600dp-mdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png new file mode 100644 index 000000000..2eb7c7ebc Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-sw600dp-xhdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-xhdpi/list_activated_holo.9.png new file mode 100644 index 000000000..eda10e612 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xhdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/list_focused_holo.9.png b/java/com/android/contacts/common/res/drawable-xhdpi/list_focused_holo.9.png new file mode 100644 index 000000000..86578be45 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xhdpi/list_focused_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/list_longpressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-xhdpi/list_longpressed_holo_light.9.png new file mode 100644 index 000000000..5532e88c2 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xhdpi/list_longpressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/list_pressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-xhdpi/list_pressed_holo_light.9.png new file mode 100644 index 000000000..f4af92657 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xhdpi/list_pressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xhdpi/list_section_divider_holo_custom.9.png b/java/com/android/contacts/common/res/drawable-xhdpi/list_section_divider_holo_custom.9.png new file mode 100644 index 000000000..8fb0636cf Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xhdpi/list_section_divider_holo_custom.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/list_activated_holo.9.png b/java/com/android/contacts/common/res/drawable-xxhdpi/list_activated_holo.9.png new file mode 100644 index 000000000..52c00ddcd Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xxhdpi/list_activated_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/list_focused_holo.9.png b/java/com/android/contacts/common/res/drawable-xxhdpi/list_focused_holo.9.png new file mode 100644 index 000000000..3e4ca684e Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xxhdpi/list_focused_holo.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/list_longpressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-xxhdpi/list_longpressed_holo_light.9.png new file mode 100644 index 000000000..230d649bf Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xxhdpi/list_longpressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable-xxhdpi/list_pressed_holo_light.9.png b/java/com/android/contacts/common/res/drawable-xxhdpi/list_pressed_holo_light.9.png new file mode 100644 index 000000000..1352a1702 Binary files /dev/null and b/java/com/android/contacts/common/res/drawable-xxhdpi/list_pressed_holo_light.9.png differ diff --git a/java/com/android/contacts/common/res/drawable/ic_back_arrow.xml b/java/com/android/contacts/common/res/drawable/ic_back_arrow.xml new file mode 100644 index 000000000..ecf704e8e --- /dev/null +++ b/java/com/android/contacts/common/res/drawable/ic_back_arrow.xml @@ -0,0 +1,20 @@ + + + \ No newline at end of file diff --git a/java/com/android/contacts/common/res/drawable/ic_call_and_share.xml b/java/com/android/contacts/common/res/drawable/ic_call_and_share.xml new file mode 100644 index 000000000..be0cdd358 --- /dev/null +++ b/java/com/android/contacts/common/res/drawable/ic_call_and_share.xml @@ -0,0 +1,22 @@ + + + + \ No newline at end of file diff --git a/java/com/android/contacts/common/res/drawable/ic_person_add_tinted_24dp.xml b/java/com/android/contacts/common/res/drawable/ic_person_add_tinted_24dp.xml new file mode 100644 index 000000000..c2538cfd1 --- /dev/null +++ b/java/com/android/contacts/common/res/drawable/ic_person_add_tinted_24dp.xml @@ -0,0 +1,20 @@ + + + diff --git a/java/com/android/contacts/common/res/drawable/ic_search_add_contact.xml b/java/com/android/contacts/common/res/drawable/ic_search_add_contact.xml new file mode 100644 index 000000000..476212d24 --- /dev/null +++ b/java/com/android/contacts/common/res/drawable/ic_search_add_contact.xml @@ -0,0 +1,20 @@ + + + + diff --git a/java/com/android/contacts/common/res/drawable/ic_search_video_call.xml b/java/com/android/contacts/common/res/drawable/ic_search_video_call.xml new file mode 100644 index 000000000..0fe0367e1 --- /dev/null +++ b/java/com/android/contacts/common/res/drawable/ic_search_video_call.xml @@ -0,0 +1,22 @@ + + + + diff --git a/java/com/android/contacts/common/res/drawable/list_selector_background_transition_holo_light.xml b/java/com/android/contacts/common/res/drawable/list_selector_background_transition_holo_light.xml new file mode 100644 index 000000000..35fff99c2 --- /dev/null +++ b/java/com/android/contacts/common/res/drawable/list_selector_background_transition_holo_light.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/java/com/android/contacts/common/res/layout/dialog_call_subject.xml b/java/com/android/contacts/common/res/layout/dialog_call_subject.xml index df8e8d299..a33b2ac85 100644 --- a/java/com/android/contacts/common/res/layout/dialog_call_subject.xml +++ b/java/com/android/contacts/common/res/layout/dialog_call_subject.xml @@ -129,7 +129,7 @@ android:layout_height="25dp" android:layout_alignParentStart="true" android:layout_centerVertical="true" - android:src="@drawable/quantum_ic_history_vd_theme_24" + android:src="@drawable/quantum_ic_history_white_24" android:tint="@color/call_subject_history_icon"/> diff --git a/java/com/android/contacts/common/res/values-af/strings.xml b/java/com/android/contacts/common/res/values-af/strings.xml index 48e4158f0..52e612da9 100644 --- a/java/com/android/contacts/common/res/values-af/strings.xml +++ b/java/com/android/contacts/common/res/values-af/strings.xml @@ -248,7 +248,7 @@ "Tik \'n nota om saam met oproep te stuur …" "STUUR EN BEL" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s-oortjie" %1$s-oortjie %2$d ongelese items. diff --git a/java/com/android/contacts/common/res/values-am/strings.xml b/java/com/android/contacts/common/res/values-am/strings.xml index 4121474f0..0283fbb0a 100644 --- a/java/com/android/contacts/common/res/values-am/strings.xml +++ b/java/com/android/contacts/common/res/values-am/strings.xml @@ -248,7 +248,7 @@ "ከጥሪ ጋር ለመላክ የማስታወሻ ጽሑፍ ይተይቡ ..." "ላክ እና ደውል" "%1$s / %2$s" - "%1$s%2$s" + "%1$s%2$s" "የ%1$s ትር።" %1$s ትር። %2$d ያልተነበቡ ንጥሎች። diff --git a/java/com/android/contacts/common/res/values-ar/strings.xml b/java/com/android/contacts/common/res/values-ar/strings.xml index 98fb057d4..a3862fe09 100644 --- a/java/com/android/contacts/common/res/values-ar/strings.xml +++ b/java/com/android/contacts/common/res/values-ar/strings.xml @@ -252,7 +252,7 @@ "اكتب ملاحظة لإرسالها مع المكالمة ..." "إرسال واتصال" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "علامة تبويب %1$s." %1$s علامة تبويب. %2$d عناصر غير مقروءة. diff --git a/java/com/android/contacts/common/res/values-az/strings.xml b/java/com/android/contacts/common/res/values-az/strings.xml index 813ada7f7..943377127 100644 --- a/java/com/android/contacts/common/res/values-az/strings.xml +++ b/java/com/android/contacts/common/res/values-az/strings.xml @@ -248,7 +248,7 @@ "Zəng ilə göndərmək üçün qeyd yazın..." "GÖNDƏRİN VƏ ZƏNG EDİN" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s tabel." %1$s tabel. %2$d oxunmamış element. diff --git a/java/com/android/contacts/common/res/values-b+sr+Latn/strings.xml b/java/com/android/contacts/common/res/values-b+sr+Latn/strings.xml index 3eccfc654..fc99567f1 100644 --- a/java/com/android/contacts/common/res/values-b+sr+Latn/strings.xml +++ b/java/com/android/contacts/common/res/values-b+sr+Latn/strings.xml @@ -249,7 +249,7 @@ "Unesite belešku koju ćete poslati uz poziv..." "POŠALJI I POZOVI" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Kartica %1$s." Kartica %1$s. %2$d nepročitana stavka. diff --git a/java/com/android/contacts/common/res/values-be/strings.xml b/java/com/android/contacts/common/res/values-be/strings.xml index 99922a874..a9652a1c1 100644 --- a/java/com/android/contacts/common/res/values-be/strings.xml +++ b/java/com/android/contacts/common/res/values-be/strings.xml @@ -250,7 +250,7 @@ "Увесці нататку для адпраўкі з выклікам…" "АДПРАВІЦЬ І ВЫКЛІКАЦЬ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Укладка %1$s." Укладка %1$s. %2$d непрачытаны элемент. diff --git a/java/com/android/contacts/common/res/values-bg/strings.xml b/java/com/android/contacts/common/res/values-bg/strings.xml index c2ebd2736..0b57eb6a5 100644 --- a/java/com/android/contacts/common/res/values-bg/strings.xml +++ b/java/com/android/contacts/common/res/values-bg/strings.xml @@ -248,7 +248,7 @@ "Напишете придружаваща бележка, която ще се изпрати при извършване на обаждането..." "ИЗПРАЩАНЕ И ОБАЖДАНЕ" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Раздел „%1$s“." Раздел „%1$s“. %2$d непрочетени елемента. diff --git a/java/com/android/contacts/common/res/values-bn/strings.xml b/java/com/android/contacts/common/res/values-bn/strings.xml index 47e9b020e..8e783d129 100644 --- a/java/com/android/contacts/common/res/values-bn/strings.xml +++ b/java/com/android/contacts/common/res/values-bn/strings.xml @@ -248,7 +248,7 @@ "কলের সাথে পাঠানোর জন্য একটি নোট লিখুন ..." "পাঠান এবং কল করুন" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ট্যাব৷" %1$s ট্যাব৷ %2$dটি অপঠিত আইটেম৷ diff --git a/java/com/android/contacts/common/res/values-bs/strings.xml b/java/com/android/contacts/common/res/values-bs/strings.xml index 305a7468a..278f6a8d0 100644 --- a/java/com/android/contacts/common/res/values-bs/strings.xml +++ b/java/com/android/contacts/common/res/values-bs/strings.xml @@ -249,7 +249,7 @@ "Upišite bilješku koja će se poslati uz poziv ..." "POŠALJI I POZOVI" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Kartica %1$s." Kartica %1$s. %2$d nepročitana stavka. diff --git a/java/com/android/contacts/common/res/values-ca/strings.xml b/java/com/android/contacts/common/res/values-ca/strings.xml index 8e8068695..ad31a01b6 100644 --- a/java/com/android/contacts/common/res/values-ca/strings.xml +++ b/java/com/android/contacts/common/res/values-ca/strings.xml @@ -248,7 +248,7 @@ "Escriu una nota per enviar-la juntament amb la trucada..." "ENVIA I TRUCA" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Pestanya %1$s" Pestanya %1$s: %2$d elements no llegits diff --git a/java/com/android/contacts/common/res/values-cs/strings.xml b/java/com/android/contacts/common/res/values-cs/strings.xml index 7aea200b1..ab54be93a 100644 --- a/java/com/android/contacts/common/res/values-cs/strings.xml +++ b/java/com/android/contacts/common/res/values-cs/strings.xml @@ -250,7 +250,7 @@ "Zadejte poznámku, která se odešle pomocí volání…" "ODESLAT A ZAVOLAT" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Karta %1$s." Karta %1$s. %2$d nepřečtené položky. diff --git a/java/com/android/contacts/common/res/values-da/strings.xml b/java/com/android/contacts/common/res/values-da/strings.xml index 430895a71..965d04a73 100644 --- a/java/com/android/contacts/common/res/values-da/strings.xml +++ b/java/com/android/contacts/common/res/values-da/strings.xml @@ -248,7 +248,7 @@ "Indtast en note, som skal sendes ved opkald..." "SEND OG RING" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Fanen %1$s." Fanen %1$s. %2$d ulæst element. diff --git a/java/com/android/contacts/common/res/values-de/strings.xml b/java/com/android/contacts/common/res/values-de/strings.xml index a46f278c1..60c5e6806 100644 --- a/java/com/android/contacts/common/res/values-de/strings.xml +++ b/java/com/android/contacts/common/res/values-de/strings.xml @@ -248,7 +248,7 @@ "Notiz eingeben, die beim Anrufen gesendet wird..." "SENDEN UND ANRUFEN" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Tab \"%1$s\"." Tab \"%1$s\". %2$d ungelesene Elemente. diff --git a/java/com/android/contacts/common/res/values-el/strings.xml b/java/com/android/contacts/common/res/values-el/strings.xml index 567b8fb52..9af3bc55a 100644 --- a/java/com/android/contacts/common/res/values-el/strings.xml +++ b/java/com/android/contacts/common/res/values-el/strings.xml @@ -248,7 +248,7 @@ "Πληκτρολογήστε μια σημείωση για αποστολή με την κλήση…" "ΑΠΟΣΤΟΛΗ ΚΑΙ ΚΛΗΣΗ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Καρτέλα %1$s." Καρτέλα %1$s. %2$d μη αναγνωσμένα στοιχεία. diff --git a/java/com/android/contacts/common/res/values-en-rAU/strings.xml b/java/com/android/contacts/common/res/values-en-rAU/strings.xml index 660993d2e..996c6ee18 100644 --- a/java/com/android/contacts/common/res/values-en-rAU/strings.xml +++ b/java/com/android/contacts/common/res/values-en-rAU/strings.xml @@ -248,7 +248,7 @@ "Type a note to send with call ..." "SEND & CALL" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s tab." %1$s tab. %2$d unread items. diff --git a/java/com/android/contacts/common/res/values-en-rGB/strings.xml b/java/com/android/contacts/common/res/values-en-rGB/strings.xml index 660993d2e..996c6ee18 100644 --- a/java/com/android/contacts/common/res/values-en-rGB/strings.xml +++ b/java/com/android/contacts/common/res/values-en-rGB/strings.xml @@ -248,7 +248,7 @@ "Type a note to send with call ..." "SEND & CALL" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s tab." %1$s tab. %2$d unread items. diff --git a/java/com/android/contacts/common/res/values-en-rIN/strings.xml b/java/com/android/contacts/common/res/values-en-rIN/strings.xml index 660993d2e..996c6ee18 100644 --- a/java/com/android/contacts/common/res/values-en-rIN/strings.xml +++ b/java/com/android/contacts/common/res/values-en-rIN/strings.xml @@ -248,7 +248,7 @@ "Type a note to send with call ..." "SEND & CALL" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s tab." %1$s tab. %2$d unread items. diff --git a/java/com/android/contacts/common/res/values-es-rUS/strings.xml b/java/com/android/contacts/common/res/values-es-rUS/strings.xml index 1c0339a6a..acb563cab 100644 --- a/java/com/android/contacts/common/res/values-es-rUS/strings.xml +++ b/java/com/android/contacts/common/res/values-es-rUS/strings.xml @@ -248,7 +248,7 @@ "Escribe una nota para enviar con la llamada…" "ENVIAR Y LLAMAR" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Pestaña %1$s." Pestaña %1$s. %2$d elementos no leídos. diff --git a/java/com/android/contacts/common/res/values-es/strings.xml b/java/com/android/contacts/common/res/values-es/strings.xml index b0956ec6a..def7a447c 100644 --- a/java/com/android/contacts/common/res/values-es/strings.xml +++ b/java/com/android/contacts/common/res/values-es/strings.xml @@ -248,7 +248,7 @@ "Escribe una nota para enviarla con la llamada..." "ENVIAR Y LLAMAR" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Pestaña %1$s." Pestaña %1$s. %2$d elementos no leídos. diff --git a/java/com/android/contacts/common/res/values-et/strings.xml b/java/com/android/contacts/common/res/values-et/strings.xml index e152f595a..c80d54214 100644 --- a/java/com/android/contacts/common/res/values-et/strings.xml +++ b/java/com/android/contacts/common/res/values-et/strings.xml @@ -248,7 +248,7 @@ "Sisestage märkus, mis koos kõnega saata ..." "SAADA JA HELISTA" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Vahekaart %1$s." Vahekaart %1$s. %2$d lugemata üksust. diff --git a/java/com/android/contacts/common/res/values-eu/strings.xml b/java/com/android/contacts/common/res/values-eu/strings.xml index 967b63540..868d668b5 100644 --- a/java/com/android/contacts/common/res/values-eu/strings.xml +++ b/java/com/android/contacts/common/res/values-eu/strings.xml @@ -248,7 +248,7 @@ "Idatzi deiarekin batera bidali beharreko oharra…" "BIDALI ETA DEITU" "%1$s/%2$s" - "%1$s: %2$s" + "%1$s%2$s" "%1$s fitxa." %1$s fitxa. Irakurri gabeko %2$d elementu. diff --git a/java/com/android/contacts/common/res/values-fa/strings.xml b/java/com/android/contacts/common/res/values-fa/strings.xml index 3650e4671..dda10647c 100644 --- a/java/com/android/contacts/common/res/values-fa/strings.xml +++ b/java/com/android/contacts/common/res/values-fa/strings.xml @@ -248,7 +248,7 @@ "یادداشتی بنویسید که همراه تماس ارسال شود…" "ارسال و تماس" "%1$s / ‏%2$s" - "%1$s %2$s" + "%1$s%2$s" "برگه «%1$s»." برگه «%1$s». %2$d مورد خوانده‌نشده. diff --git a/java/com/android/contacts/common/res/values-fi/strings.xml b/java/com/android/contacts/common/res/values-fi/strings.xml index b7739dee8..b8065b2bf 100644 --- a/java/com/android/contacts/common/res/values-fi/strings.xml +++ b/java/com/android/contacts/common/res/values-fi/strings.xml @@ -248,7 +248,7 @@ "Kirjoita muistiinpano lähetettäväksi puhelun kanssa…" "LÄHETÄ JA SOITA" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s-välilehti." %1$s-välilehti. %2$d lukematonta kohdetta. diff --git a/java/com/android/contacts/common/res/values-fr-rCA/strings.xml b/java/com/android/contacts/common/res/values-fr-rCA/strings.xml index c20b8827e..82f549292 100644 --- a/java/com/android/contacts/common/res/values-fr-rCA/strings.xml +++ b/java/com/android/contacts/common/res/values-fr-rCA/strings.xml @@ -248,7 +248,7 @@ "Tapez une note à envoyer avec l\'appel..." "ENVOYER ET APPELER" "%1$s/%2$s" - "%1$s : %2$s" + "%1$s%2$s" "Onglet %1$s." Onglet %1$s. %2$d élément non lu. diff --git a/java/com/android/contacts/common/res/values-fr/strings.xml b/java/com/android/contacts/common/res/values-fr/strings.xml index 93b52dcf9..474d721dc 100644 --- a/java/com/android/contacts/common/res/values-fr/strings.xml +++ b/java/com/android/contacts/common/res/values-fr/strings.xml @@ -248,7 +248,7 @@ "Saisissez une note pour accompagner l\'appel..." "ENVOYER ET APPELER" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Onglet %1$s." Onglet %1$s. %2$d élément non lu. diff --git a/java/com/android/contacts/common/res/values-gl/strings.xml b/java/com/android/contacts/common/res/values-gl/strings.xml index 4aa1fc1f7..4a3262a2b 100644 --- a/java/com/android/contacts/common/res/values-gl/strings.xml +++ b/java/com/android/contacts/common/res/values-gl/strings.xml @@ -248,7 +248,7 @@ "Escribe unha nota para enviala coa chamada…" "ENVIAR E CHAMAR" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Pestana %1$s." Pestana %1$s. %2$d elementos non lidos. diff --git a/java/com/android/contacts/common/res/values-gu/strings.xml b/java/com/android/contacts/common/res/values-gu/strings.xml index 8bbfb3a2b..536aceb9c 100644 --- a/java/com/android/contacts/common/res/values-gu/strings.xml +++ b/java/com/android/contacts/common/res/values-gu/strings.xml @@ -248,7 +248,7 @@ "કૉલ સાથે મોકલવા માટે એક નોંધ લખો ..." "મોકલો અને કૉલ કરો" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ટૅબ." %1$s ટૅબ. %2$d વાંચ્યા વગરની આઇટમ. diff --git a/java/com/android/contacts/common/res/values-hi/strings.xml b/java/com/android/contacts/common/res/values-hi/strings.xml index c1af9b7ff..c61e195cb 100644 --- a/java/com/android/contacts/common/res/values-hi/strings.xml +++ b/java/com/android/contacts/common/res/values-hi/strings.xml @@ -248,7 +248,7 @@ "कॉल के साथ भेजने के लिए नोट लिखें ..." "भेजें और कॉल करें" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s टैब." %1$s टैब. %2$d नहीं पढ़े गए आइटम. diff --git a/java/com/android/contacts/common/res/values-hr/strings.xml b/java/com/android/contacts/common/res/values-hr/strings.xml index a60c884c1..66986dd38 100644 --- a/java/com/android/contacts/common/res/values-hr/strings.xml +++ b/java/com/android/contacts/common/res/values-hr/strings.xml @@ -249,7 +249,7 @@ "Napišite bilješku koju ćete poslati uz poziv..." "POŠALJI I NAZOVI" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Kartica %1$s." Kartica %1$s. %2$d nepročitana stavka. diff --git a/java/com/android/contacts/common/res/values-hu/strings.xml b/java/com/android/contacts/common/res/values-hu/strings.xml index 19fbbe844..22bf5094c 100644 --- a/java/com/android/contacts/common/res/values-hu/strings.xml +++ b/java/com/android/contacts/common/res/values-hu/strings.xml @@ -248,7 +248,7 @@ "Írjon üzenetet, amelyet elküldhetünk a hívással együtt…" "KÜLDÉS ÉS HÍVÁS" "%2$s/%1$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s lap." %1$s lap. %2$d olvasatlan elem. diff --git a/java/com/android/contacts/common/res/values-hy/strings.xml b/java/com/android/contacts/common/res/values-hy/strings.xml index b3ed5e531..85c902347 100644 --- a/java/com/android/contacts/common/res/values-hy/strings.xml +++ b/java/com/android/contacts/common/res/values-hy/strings.xml @@ -248,7 +248,7 @@ "Մուտքագրեք նշում՝ զանգի հետ ուղարկելու համար ..." "ՈՒՂԱՐԿԵԼ ԵՎ ԶԱՆԳԵԼ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ներդիր:" %1$s tab. %2$d unread items. diff --git a/java/com/android/contacts/common/res/values-in/strings.xml b/java/com/android/contacts/common/res/values-in/strings.xml index 13252a5b0..d7ee8bfb9 100644 --- a/java/com/android/contacts/common/res/values-in/strings.xml +++ b/java/com/android/contacts/common/res/values-in/strings.xml @@ -248,7 +248,7 @@ "Ketik catatan untuk dikirim dengan panggilan telepon ..." "KIRIM & TELEPON" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Tab %1$s." Tab %1$s. %2$d item belum dibaca. diff --git a/java/com/android/contacts/common/res/values-is/strings.xml b/java/com/android/contacts/common/res/values-is/strings.xml index 569c10f3e..26c1aefd1 100644 --- a/java/com/android/contacts/common/res/values-is/strings.xml +++ b/java/com/android/contacts/common/res/values-is/strings.xml @@ -248,7 +248,7 @@ "Sláðu inn texta til að senda með símtalinu..." "SENDA OG HRINGJA" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Flipinn %1$s." Flipinn %1$s. %2$d ólesið atriði. diff --git a/java/com/android/contacts/common/res/values-it/strings.xml b/java/com/android/contacts/common/res/values-it/strings.xml index 342cd61df..8ea564854 100644 --- a/java/com/android/contacts/common/res/values-it/strings.xml +++ b/java/com/android/contacts/common/res/values-it/strings.xml @@ -248,7 +248,7 @@ "Digita una nota da inviare con la chiamata..." "INVIA E CHIAMA" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Scheda %1$s." Scheda %1$s. %2$d elementi da leggere. diff --git a/java/com/android/contacts/common/res/values-iw/strings.xml b/java/com/android/contacts/common/res/values-iw/strings.xml index e0d8e5dfb..2a6b2c189 100644 --- a/java/com/android/contacts/common/res/values-iw/strings.xml +++ b/java/com/android/contacts/common/res/values-iw/strings.xml @@ -250,7 +250,7 @@ "הקלד הערה לשליחה עם השיחה..." "שלח והתקשר" "%1$s / %2$s" - "‎%1$s %2$s‎" + "%1$s%2$s" "הכרטיסייה %1$s." הכרטיסייה %1$s. %2$d פריטים שלא נקראו. diff --git a/java/com/android/contacts/common/res/values-ja/strings.xml b/java/com/android/contacts/common/res/values-ja/strings.xml index e224a38a2..3271b46bb 100644 --- a/java/com/android/contacts/common/res/values-ja/strings.xml +++ b/java/com/android/contacts/common/res/values-ja/strings.xml @@ -248,7 +248,7 @@ "発信時に送信するメモを入力..." "送信 / 通話" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s タブ。" %1$s タブ。%2$d 件の未読項目。 diff --git a/java/com/android/contacts/common/res/values-ka/strings.xml b/java/com/android/contacts/common/res/values-ka/strings.xml index 86273b32e..ebbee6e33 100644 --- a/java/com/android/contacts/common/res/values-ka/strings.xml +++ b/java/com/android/contacts/common/res/values-ka/strings.xml @@ -248,7 +248,7 @@ "აკრიფეთ შენიშვნა ზართან ერთად გასაგზავნად ..." "გაგზავნა და დარეკვა" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ცხრ." %1$s ცხრ. %2$d წაუკითხავი ერთეული. diff --git a/java/com/android/contacts/common/res/values-kk/strings.xml b/java/com/android/contacts/common/res/values-kk/strings.xml index 45df334f9..327532abc 100644 --- a/java/com/android/contacts/common/res/values-kk/strings.xml +++ b/java/com/android/contacts/common/res/values-kk/strings.xml @@ -248,7 +248,7 @@ "Қоңыраумен жіберу үшін ескертпе теріңіз ..." "ЖІБЕРУ ЖӘНЕ ҚОҢЫРАУ ШАЛУ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s қойындысы." %1$s қойындысы. %2$d оқылмаған элемент. diff --git a/java/com/android/contacts/common/res/values-km/strings.xml b/java/com/android/contacts/common/res/values-km/strings.xml index 8d4ba2aff..503016792 100644 --- a/java/com/android/contacts/common/res/values-km/strings.xml +++ b/java/com/android/contacts/common/res/values-km/strings.xml @@ -248,7 +248,7 @@ "វាយបញ្ចូលចំណាំដែលត្រូវផ្ញើជាមួយការហៅទូរស័ព្ទ ..." "ផ្ញើ & ហៅ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "ផ្ទាំង %1$s។" ផ្ទាំង %1$s។ ធាតុមិនទាន់​អាន %2$d diff --git a/java/com/android/contacts/common/res/values-kn/strings.xml b/java/com/android/contacts/common/res/values-kn/strings.xml index 0dfdaa609..446e078a6 100644 --- a/java/com/android/contacts/common/res/values-kn/strings.xml +++ b/java/com/android/contacts/common/res/values-kn/strings.xml @@ -248,7 +248,7 @@ "ಕರೆ ಕಳುಹಿಸಲು ಟಿಪ್ಪಣಿಯನ್ನು ಟೈಪ್ ಮಾಡಿ ..." "ಕಳುಹಿಸು ಮತ್ತು ಕರೆಮಾಡು" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ಟ್ಯಾಬ್." %1$s ಟ್ಯಾಬ್. %2$d ಓದದಿರುವ ಐಟಂಗಳು. diff --git a/java/com/android/contacts/common/res/values-ko/strings.xml b/java/com/android/contacts/common/res/values-ko/strings.xml index 13edcc11e..4c2bd4f62 100644 --- a/java/com/android/contacts/common/res/values-ko/strings.xml +++ b/java/com/android/contacts/common/res/values-ko/strings.xml @@ -248,7 +248,7 @@ "통화에 함께 전송할 메모를 입력하세요..." "보내기 및 전화 걸기" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s 탭" %1$s 탭. 읽지 않은 항목 %2$d diff --git a/java/com/android/contacts/common/res/values-ky/strings.xml b/java/com/android/contacts/common/res/values-ky/strings.xml index 43e12e77e..e5bab4550 100644 --- a/java/com/android/contacts/common/res/values-ky/strings.xml +++ b/java/com/android/contacts/common/res/values-ky/strings.xml @@ -248,7 +248,7 @@ "Чалуу менен жөнөтүлө турган кыска жазууну териңиз …" "ЖӨНӨТҮҮ ЖАНА ЧАЛУУ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "\"%1$s\" өтмөгү." \"%1$s\" өтмөгү. %2$d нерсе окула элек. diff --git a/java/com/android/contacts/common/res/values-lo/strings.xml b/java/com/android/contacts/common/res/values-lo/strings.xml index d98ec8b90..c649539d4 100644 --- a/java/com/android/contacts/common/res/values-lo/strings.xml +++ b/java/com/android/contacts/common/res/values-lo/strings.xml @@ -248,7 +248,7 @@ "ພິມ​ບັນ​ທຶກ ເພື່ອ​ສົ່ງ​ກັບ​ການ​ໂທ ..." "ສົ່ງ ແລະ ໂທ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "ແຖບ %1$s." ແຖບ %1$s. %2$d ລາຍການບໍ່ທັນໄດ້ອ່ານ. diff --git a/java/com/android/contacts/common/res/values-lt/strings.xml b/java/com/android/contacts/common/res/values-lt/strings.xml index 53773b25a..b74bd99ee 100644 --- a/java/com/android/contacts/common/res/values-lt/strings.xml +++ b/java/com/android/contacts/common/res/values-lt/strings.xml @@ -250,7 +250,7 @@ "Įveskite užrašą, kurį galima išsiųsti skambinant..." "SIŲSTI IR SKAMBINTI" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Skirtukas „%1$s“." Skirtukas „%1$s“. %2$d neskaitytas elementas. diff --git a/java/com/android/contacts/common/res/values-lv/strings.xml b/java/com/android/contacts/common/res/values-lv/strings.xml index cc5690176..c09a27f5d 100644 --- a/java/com/android/contacts/common/res/values-lv/strings.xml +++ b/java/com/android/contacts/common/res/values-lv/strings.xml @@ -249,7 +249,7 @@ "Ierakstiet piezīmi, ko nosūtīt ar zvanu..." "SŪTĪT UN ZVANĪT" "%1$s no %2$s" - "%1$s: %2$s" + "%1$s • %2$s" "Cilne “%1$s”" Cilne “%1$s”. %2$d nelasīti vienumi. diff --git a/java/com/android/contacts/common/res/values-mk/strings.xml b/java/com/android/contacts/common/res/values-mk/strings.xml index f6a4af8b1..da1530f2a 100644 --- a/java/com/android/contacts/common/res/values-mk/strings.xml +++ b/java/com/android/contacts/common/res/values-mk/strings.xml @@ -248,7 +248,7 @@ "Напишете белешка да се испрати со повикот..." "ИСПРАТИ И ПОВИКАЈ" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Картичка %1$s." Картичка %1$s. %2$d непрочитана ставка. diff --git a/java/com/android/contacts/common/res/values-ml/strings.xml b/java/com/android/contacts/common/res/values-ml/strings.xml index dfc21ffb9..7e927499a 100644 --- a/java/com/android/contacts/common/res/values-ml/strings.xml +++ b/java/com/android/contacts/common/res/values-ml/strings.xml @@ -248,7 +248,7 @@ "കോളിനൊപ്പം അയയ്ക്കുന്നതിന് ഒരു കുറിപ്പ് ടൈപ്പുചെയ്യുക ..." "അയയ്‌ക്കുകയും വിളിക്കുകയും ചെയ്യുക" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ടാബ്." %1$s ടാബ്. %2$d വായിക്കാത്ത ഇനങ്ങൾ. diff --git a/java/com/android/contacts/common/res/values-mn/strings.xml b/java/com/android/contacts/common/res/values-mn/strings.xml index a5c2afd2c..f0a8c9245 100644 --- a/java/com/android/contacts/common/res/values-mn/strings.xml +++ b/java/com/android/contacts/common/res/values-mn/strings.xml @@ -248,7 +248,7 @@ "Дуудлаганд илгээх тэмдэглэл бичнэ үү..." "ИЛГЭЭХ, ДУУДЛАГА" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s таб." %1$s таб. %2$d уншаагүй зүйл. diff --git a/java/com/android/contacts/common/res/values-mr/strings.xml b/java/com/android/contacts/common/res/values-mr/strings.xml index d632bb1be..96504c7bc 100644 --- a/java/com/android/contacts/common/res/values-mr/strings.xml +++ b/java/com/android/contacts/common/res/values-mr/strings.xml @@ -248,7 +248,7 @@ "कॉलसह पाठविण्‍यासाठी एक टीप टाइप करा..." "पाठवा आणि कॉल करा" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s टॅब." %1$s टॅब. %2$d न वाचलेला आयटम. diff --git a/java/com/android/contacts/common/res/values-ms/strings.xml b/java/com/android/contacts/common/res/values-ms/strings.xml index 24ff73767..60d2b7acc 100644 --- a/java/com/android/contacts/common/res/values-ms/strings.xml +++ b/java/com/android/contacts/common/res/values-ms/strings.xml @@ -248,7 +248,7 @@ "Taip nota untuk dihantar dengan panggilan…" "HANTAR & PANGGIL" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Tab %1$s." Tab %1$s. %2$d item belum dibaca. diff --git a/java/com/android/contacts/common/res/values-my/strings.xml b/java/com/android/contacts/common/res/values-my/strings.xml index c0cb020f8..d81e8950d 100644 --- a/java/com/android/contacts/common/res/values-my/strings.xml +++ b/java/com/android/contacts/common/res/values-my/strings.xml @@ -248,7 +248,7 @@ "ခေါ်ဆိုမှုဖြင့် ပေးပို့ရန် မှတ်စုတစ်ခု ရိုက်ပါ ..." "ပေးပို့ပြီး ခေါ်ပါ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s တဘ်။" %1$s တဘ်။ မဖတ်ရသေးသည့် အရာ %2$d ခု။ diff --git a/java/com/android/contacts/common/res/values-nb/strings.xml b/java/com/android/contacts/common/res/values-nb/strings.xml index 80507fce1..7085cb381 100644 --- a/java/com/android/contacts/common/res/values-nb/strings.xml +++ b/java/com/android/contacts/common/res/values-nb/strings.xml @@ -248,7 +248,7 @@ "Skriv et notat du vil sende med anropet …" "SEND OG RING" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s-fanen." %1$s-fanen. %2$d uleste elementer. diff --git a/java/com/android/contacts/common/res/values-ne/strings.xml b/java/com/android/contacts/common/res/values-ne/strings.xml index 2459c8aba..1f54232af 100644 --- a/java/com/android/contacts/common/res/values-ne/strings.xml +++ b/java/com/android/contacts/common/res/values-ne/strings.xml @@ -248,7 +248,7 @@ "यस कलसँग पठाउन एक टिप्पणी टाइप गर्नुहोस्" "पठाउनुहोस् र कल गर्नुहोस्" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ट्याब।" %1$s ट्याब। %2$d नपढिएका वस्तुहरू। diff --git a/java/com/android/contacts/common/res/values-nl/strings.xml b/java/com/android/contacts/common/res/values-nl/strings.xml index c92249b61..9d41899f1 100644 --- a/java/com/android/contacts/common/res/values-nl/strings.xml +++ b/java/com/android/contacts/common/res/values-nl/strings.xml @@ -248,7 +248,7 @@ "Typ een notitie om te verzenden met de oproep..." "VERZENDEN EN BELLEN" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Tabblad %1$s." Tabblad %1$s. %2$d ongelezen items. diff --git a/java/com/android/contacts/common/res/values-no/strings.xml b/java/com/android/contacts/common/res/values-no/strings.xml index 80507fce1..7085cb381 100644 --- a/java/com/android/contacts/common/res/values-no/strings.xml +++ b/java/com/android/contacts/common/res/values-no/strings.xml @@ -248,7 +248,7 @@ "Skriv et notat du vil sende med anropet …" "SEND OG RING" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s-fanen." %1$s-fanen. %2$d uleste elementer. diff --git a/java/com/android/contacts/common/res/values-pa/strings.xml b/java/com/android/contacts/common/res/values-pa/strings.xml index 6401993c2..c96510cc7 100644 --- a/java/com/android/contacts/common/res/values-pa/strings.xml +++ b/java/com/android/contacts/common/res/values-pa/strings.xml @@ -248,7 +248,7 @@ "ਕਾਲ ਦੇ ਨਾਲ ਭੇਜਣ ਲਈ ਕੋਈ ਨੋਟ ਟਾਈਪ ਕਰੋ ..." "ਭੇਜੋ ਅਤੇ ਕਾਲ ਕਰੋ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ਟੈਬ।" %1$s ਟੈਬ। %2$d ਅਣ-ਪੜ੍ਹੀਆਂ ਆਈਟਮਾਂ। diff --git a/java/com/android/contacts/common/res/values-pl/strings.xml b/java/com/android/contacts/common/res/values-pl/strings.xml index c10bb1fd9..c4e65f556 100644 --- a/java/com/android/contacts/common/res/values-pl/strings.xml +++ b/java/com/android/contacts/common/res/values-pl/strings.xml @@ -250,7 +250,7 @@ "Wpisz notatkę, którą chcesz wysłać razem z połączeniem..." "WYŚLIJ I ZADZWOŃ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Karta %1$s." Karta %1$s. %2$d nieprzeczytane elementy. diff --git a/java/com/android/contacts/common/res/values-pt-rBR/strings.xml b/java/com/android/contacts/common/res/values-pt-rBR/strings.xml index 95bbb0b5d..ee2b87c68 100644 --- a/java/com/android/contacts/common/res/values-pt-rBR/strings.xml +++ b/java/com/android/contacts/common/res/values-pt-rBR/strings.xml @@ -248,7 +248,7 @@ "Escreva uma nota para enviar com a chamada..." "ENVIAR E LIGAR" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Guia \"%1$s\"." Guia \"%1$s\". %2$d item não lido. diff --git a/java/com/android/contacts/common/res/values-pt-rPT/strings.xml b/java/com/android/contacts/common/res/values-pt-rPT/strings.xml index c3b50e0bb..44bba149d 100644 --- a/java/com/android/contacts/common/res/values-pt-rPT/strings.xml +++ b/java/com/android/contacts/common/res/values-pt-rPT/strings.xml @@ -248,7 +248,7 @@ "Escrever uma nota para enviar com a chamada..." "ENVIAR E LIGAR" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Separador %1$s." %1$s tab. %2$d unread items. diff --git a/java/com/android/contacts/common/res/values-pt/strings.xml b/java/com/android/contacts/common/res/values-pt/strings.xml index 95bbb0b5d..ee2b87c68 100644 --- a/java/com/android/contacts/common/res/values-pt/strings.xml +++ b/java/com/android/contacts/common/res/values-pt/strings.xml @@ -248,7 +248,7 @@ "Escreva uma nota para enviar com a chamada..." "ENVIAR E LIGAR" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Guia \"%1$s\"." Guia \"%1$s\". %2$d item não lido. diff --git a/java/com/android/contacts/common/res/values-ro/strings.xml b/java/com/android/contacts/common/res/values-ro/strings.xml index da0d5da95..358e7af93 100644 --- a/java/com/android/contacts/common/res/values-ro/strings.xml +++ b/java/com/android/contacts/common/res/values-ro/strings.xml @@ -249,7 +249,7 @@ "Introduceți o notă ca să o trimiteți împreună cu apelul..." "TRIMITEȚI ȘI APELAȚI" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Fila %1$s" Fila %1$s. %2$d elemente necitite. diff --git a/java/com/android/contacts/common/res/values-ru/strings.xml b/java/com/android/contacts/common/res/values-ru/strings.xml index f4e1d847b..3442f2c60 100644 --- a/java/com/android/contacts/common/res/values-ru/strings.xml +++ b/java/com/android/contacts/common/res/values-ru/strings.xml @@ -250,7 +250,7 @@ "Введите текст…" "ОТПРАВИТЬ И ПОЗВОНИТЬ" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Вкладка \"%1$s\"." Вкладка %1$s. %2$d непрочитанный элемент. diff --git a/java/com/android/contacts/common/res/values-si/strings.xml b/java/com/android/contacts/common/res/values-si/strings.xml index fc121f6af..72ca32d9c 100644 --- a/java/com/android/contacts/common/res/values-si/strings.xml +++ b/java/com/android/contacts/common/res/values-si/strings.xml @@ -248,7 +248,7 @@ "ඇමතුම සමග යැවීමට සටහනක් ටයිප් කරන්න ..." "යවන්න සහ අමතන්න" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ටැබය." %1$s ටැබය. නොකියවූ අයිතම %2$d. diff --git a/java/com/android/contacts/common/res/values-sk/strings.xml b/java/com/android/contacts/common/res/values-sk/strings.xml index 90a4752fd..b85f63f1b 100644 --- a/java/com/android/contacts/common/res/values-sk/strings.xml +++ b/java/com/android/contacts/common/res/values-sk/strings.xml @@ -250,7 +250,7 @@ "Napíšte poznámku, ktorá sa odošle s hovorom..." "ODOSLAŤ A VOLAŤ" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Karta %1$s." Karta %1$s. %2$d neprečítané položky. diff --git a/java/com/android/contacts/common/res/values-sl/strings.xml b/java/com/android/contacts/common/res/values-sl/strings.xml index ef2e1a3f0..f4c39dea9 100644 --- a/java/com/android/contacts/common/res/values-sl/strings.xml +++ b/java/com/android/contacts/common/res/values-sl/strings.xml @@ -250,7 +250,7 @@ "Vnesite zapisek, ki ga želite poslati s klicem ..." "POŠLJI IN KLIČI" "%1$s/%2$s" - "%1$s: %2$s" + "%1$s%2$s" "Zavihek »%1$s«." Zavihek »%1$s«. %2$d neprebran element. diff --git a/java/com/android/contacts/common/res/values-sq/strings.xml b/java/com/android/contacts/common/res/values-sq/strings.xml index 301149201..553c46df5 100644 --- a/java/com/android/contacts/common/res/values-sq/strings.xml +++ b/java/com/android/contacts/common/res/values-sq/strings.xml @@ -248,7 +248,7 @@ "Shkruaj një shënim për të dërguar një telefonatë..." "DËRGO DHE TELEFONO" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Skeda %1$s." Skeda %1$s. %2$d artikuj të palexuar. diff --git a/java/com/android/contacts/common/res/values-sr/strings.xml b/java/com/android/contacts/common/res/values-sr/strings.xml index 25adf7bc5..99997e656 100644 --- a/java/com/android/contacts/common/res/values-sr/strings.xml +++ b/java/com/android/contacts/common/res/values-sr/strings.xml @@ -249,7 +249,7 @@ "Унесите белешку коју ћете послати уз позив..." "ПОШАЉИ И ПОЗОВИ" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Картица %1$s." Картица %1$s. %2$d непрочитана ставка. diff --git a/java/com/android/contacts/common/res/values-sv/strings.xml b/java/com/android/contacts/common/res/values-sv/strings.xml index ee748a42c..79e56e504 100644 --- a/java/com/android/contacts/common/res/values-sv/strings.xml +++ b/java/com/android/contacts/common/res/values-sv/strings.xml @@ -248,7 +248,7 @@ "Gör en anteckning som skickas när du ringer …" "SKICKA OCH RING" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "Fliken %1$s." Fliken %1$s. %2$d olästa poster. diff --git a/java/com/android/contacts/common/res/values-sw/strings.xml b/java/com/android/contacts/common/res/values-sw/strings.xml index 7bd08353f..4d30271f3 100644 --- a/java/com/android/contacts/common/res/values-sw/strings.xml +++ b/java/com/android/contacts/common/res/values-sw/strings.xml @@ -248,7 +248,7 @@ "Andika dokezo litakaloambatana na simu utakayopiga ..." "TUMA na UPIGE SIMU" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Kichupo cha %1$s." Kichupo cha %1$s. Vipengee %2$d havijasomwa. diff --git a/java/com/android/contacts/common/res/values-ta/strings.xml b/java/com/android/contacts/common/res/values-ta/strings.xml index d5b3f9305..e041a999d 100644 --- a/java/com/android/contacts/common/res/values-ta/strings.xml +++ b/java/com/android/contacts/common/res/values-ta/strings.xml @@ -248,7 +248,7 @@ "அழைப்புடன் சேர்த்து அனுப்ப, குறிப்பை உள்ளிடவும்..." "அனுப்பி அழை" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s தாவல்." %1$s தாவல். படிக்காதவை (%2$d). diff --git a/java/com/android/contacts/common/res/values-te/strings.xml b/java/com/android/contacts/common/res/values-te/strings.xml index 4126ddc0f..6dd0e2143 100644 --- a/java/com/android/contacts/common/res/values-te/strings.xml +++ b/java/com/android/contacts/common/res/values-te/strings.xml @@ -248,7 +248,7 @@ "కాల్‌తో పాటు పంపడానికి గమనికను టైప్ చేయండి ..." "పంపు & కాల్ చేయి" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ట్యాబ్." %1$s ట్యాబ్. %2$d చదవని అంశాలు. diff --git a/java/com/android/contacts/common/res/values-th/strings.xml b/java/com/android/contacts/common/res/values-th/strings.xml index 2d52f34fb..b979e7424 100644 --- a/java/com/android/contacts/common/res/values-th/strings.xml +++ b/java/com/android/contacts/common/res/values-th/strings.xml @@ -248,7 +248,7 @@ "พิมพ์โน้ตเพื่อส่งพร้อมการโทร ..." "ส่งและโทร" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "แท็บ %1$s" แท็บ %1$s ยังไม่อ่าน %2$d รายการ diff --git a/java/com/android/contacts/common/res/values-tl/strings.xml b/java/com/android/contacts/common/res/values-tl/strings.xml index 30978db4e..62caef820 100644 --- a/java/com/android/contacts/common/res/values-tl/strings.xml +++ b/java/com/android/contacts/common/res/values-tl/strings.xml @@ -248,7 +248,7 @@ "Mag-type ng isang tala na ipadadala kasama ng tawag ..." "IPADALA AT TAWAGAN" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Tab ng %1$s." Tab ng %1$s. %2$d hindi pa nababasang item. diff --git a/java/com/android/contacts/common/res/values-tr/strings.xml b/java/com/android/contacts/common/res/values-tr/strings.xml index c18097aac..ce3c109d2 100644 --- a/java/com/android/contacts/common/res/values-tr/strings.xml +++ b/java/com/android/contacts/common/res/values-tr/strings.xml @@ -248,7 +248,7 @@ "Çağrıyla göndermek için bir not yazın..." "GÖNDER VE ARA" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s sekmesi." %1$s sekmesi. %2$d okunmamış öğe. diff --git a/java/com/android/contacts/common/res/values-uk/strings.xml b/java/com/android/contacts/common/res/values-uk/strings.xml index 94d09db12..f6d1e4731 100644 --- a/java/com/android/contacts/common/res/values-uk/strings.xml +++ b/java/com/android/contacts/common/res/values-uk/strings.xml @@ -250,7 +250,7 @@ "Введіть нотатку, яку хочете надіслати під час дзвінка…" "ЗАТЕЛЕФОНУВАТИ Й НАДІСЛАТИ" "%1$s з %2$s" - "%1$s %2$s" + "%1$s%2$s" "Вкладка \"%1$s\"." Вкладка \"%1$s\". %2$d непрочитаний елемент. diff --git a/java/com/android/contacts/common/res/values-ur/strings.xml b/java/com/android/contacts/common/res/values-ur/strings.xml index 75c761f2d..b34c78d95 100644 --- a/java/com/android/contacts/common/res/values-ur/strings.xml +++ b/java/com/android/contacts/common/res/values-ur/strings.xml @@ -248,7 +248,7 @@ "کال کے ساتھ بھیجنے کیلئے ایک نوٹ ٹائپ کریں…" "بھیجیں اور کال کریں" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ٹیب۔" %1$s ٹیب۔ %2$d بغیر پڑھی ہوئی آئٹمز۔ diff --git a/java/com/android/contacts/common/res/values-uz/strings.xml b/java/com/android/contacts/common/res/values-uz/strings.xml index 69b07d135..44bfd9999 100644 --- a/java/com/android/contacts/common/res/values-uz/strings.xml +++ b/java/com/android/contacts/common/res/values-uz/strings.xml @@ -248,7 +248,7 @@ "Qo‘ng‘iroqqa qo‘shib yuborish uchun izoh yozing ..." "YUBORISH va QO‘NG‘IROQ QILISH" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ichki oynasi." %1$s ichki oynasi. %2$d ta o‘qilmagan narsa. diff --git a/java/com/android/contacts/common/res/values-vi/strings.xml b/java/com/android/contacts/common/res/values-vi/strings.xml index c1675738a..dadb78a7d 100644 --- a/java/com/android/contacts/common/res/values-vi/strings.xml +++ b/java/com/android/contacts/common/res/values-vi/strings.xml @@ -248,7 +248,7 @@ "Nhập ghi chú để gửi kèm cuộc gọi..." "GỬI và GỌI" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "Tab %1$s." Tab %1$s. %2$d mục chưa đọc. diff --git a/java/com/android/contacts/common/res/values-zh-rCN/strings.xml b/java/com/android/contacts/common/res/values-zh-rCN/strings.xml index 367b78f1c..161638e66 100644 --- a/java/com/android/contacts/common/res/values-zh-rCN/strings.xml +++ b/java/com/android/contacts/common/res/values-zh-rCN/strings.xml @@ -248,7 +248,7 @@ "输入要在拨打电话时发送的备注…" "发送并拨打电话" "%1$s / %2$s" - "%1$s%2$s" + "%1$s%2$s" "%1$s标签。" %1$s标签。%2$d 项未读内容。 diff --git a/java/com/android/contacts/common/res/values-zh-rHK/strings.xml b/java/com/android/contacts/common/res/values-zh-rHK/strings.xml index 733d9809c..9952ab626 100644 --- a/java/com/android/contacts/common/res/values-zh-rHK/strings.xml +++ b/java/com/android/contacts/common/res/values-zh-rHK/strings.xml @@ -248,7 +248,7 @@ "撥號時可以書寫和傳送筆記…" "傳送和撥號" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "「%1$s」標籤。" %1$s」標籤 (%2$d 個未讀取項目)。 diff --git a/java/com/android/contacts/common/res/values-zh-rTW/strings.xml b/java/com/android/contacts/common/res/values-zh-rTW/strings.xml index af7843ffd..e5ad61fac 100644 --- a/java/com/android/contacts/common/res/values-zh-rTW/strings.xml +++ b/java/com/android/contacts/common/res/values-zh-rTW/strings.xml @@ -248,7 +248,7 @@ "輸入可在撥號時傳送的備註..." "傳送並撥打" "%1$s/%2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s分頁。" %1$s分頁。%2$d 個未讀項目。 diff --git a/java/com/android/contacts/common/res/values-zu/strings.xml b/java/com/android/contacts/common/res/values-zu/strings.xml index 5906ee175..adca1399c 100644 --- a/java/com/android/contacts/common/res/values-zu/strings.xml +++ b/java/com/android/contacts/common/res/values-zu/strings.xml @@ -248,7 +248,7 @@ "Thayipha inothi ukuthumela nekholi ..." "THUMELA FUTHI YENZA IKHOLI" "%1$s / %2$s" - "%1$s %2$s" + "%1$s%2$s" "%1$s ithebhu." %1$s ithebhu. %2$d izinto ezingafundiwe. diff --git a/java/com/android/contacts/common/res/values/ids.xml b/java/com/android/contacts/common/res/values/ids.xml index 65969c43f..871f5a636 100644 --- a/java/com/android/contacts/common/res/values/ids.xml +++ b/java/com/android/contacts/common/res/values/ids.xml @@ -27,5 +27,4 @@ - diff --git a/java/com/android/contacts/common/res/values/strings.xml b/java/com/android/contacts/common/res/values/strings.xml index 371d5b424..9ac9fef28 100644 --- a/java/com/android/contacts/common/res/values/strings.xml +++ b/java/com/android/contacts/common/res/values/strings.xml @@ -744,9 +744,9 @@ compared to the character limit. Example: 2 / 64 --> %1$s / %2$s - - %1$s %2$s + + %1$s%2$s %1$s tab. diff --git a/java/com/android/contacts/common/res/values/styles.xml b/java/com/android/contacts/common/res/values/styles.xml index bb19afe82..07d4a0225 100644 --- a/java/com/android/contacts/common/res/values/styles.xml +++ b/java/com/android/contacts/common/res/values/styles.xml @@ -43,6 +43,7 @@ background and text color. See also android:style/Widget.Holo.TextView.ListSepar wrap_content @dimen/list_section_divider_min_height + @drawable/list_section_divider_holo_custom @style/DirectoryHeaderStyle center_vertical 8dip diff --git a/java/com/android/contacts/common/util/AccountFilterUtil.java b/java/com/android/contacts/common/util/AccountFilterUtil.java index 25f937c5e..18743c65e 100644 --- a/java/com/android/contacts/common/util/AccountFilterUtil.java +++ b/java/com/android/contacts/common/util/AccountFilterUtil.java @@ -16,15 +16,35 @@ package com.android.contacts.common.util; +import android.app.Activity; import android.content.Context; +import android.content.Intent; +import android.util.Log; import android.view.View; import android.widget.TextView; import com.android.contacts.common.R; import com.android.contacts.common.list.ContactListFilter; +import com.android.contacts.common.list.ContactListFilterController; /** Utility class for account filter manipulation. */ public class AccountFilterUtil { + public static final String EXTRA_CONTACT_LIST_FILTER = "contactListFilter"; + private static final String TAG = AccountFilterUtil.class.getSimpleName(); + + /** + * Find TextView with the id "account_filter_header" and set correct text for the account filter + * header. + * + * @param filterContainer View containing TextView with id "account_filter_header" + * @return true when header text is set in the call. You may use this for conditionally showing or + * hiding this entire view. + */ + public static boolean updateAccountFilterTitleForPeople( + View filterContainer, ContactListFilter filter, boolean showTitleForAllAccounts) { + return updateAccountFilterTitle(filterContainer, filter, showTitleForAllAccounts, false); + } + /** * Similar to {@link #updateAccountFilterTitleForPeople(View, ContactListFilter, boolean, * boolean)}, but for Phone UI. @@ -58,6 +78,8 @@ public class AccountFilterUtil { } else if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) { headerTextView.setText(R.string.listCustomView); textWasSet = true; + } else { + Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected."); } } else { if (filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) { @@ -75,9 +97,29 @@ public class AccountFilterUtil { } else if (filter.filterType == ContactListFilter.FILTER_TYPE_SINGLE_CONTACT) { headerTextView.setText(R.string.listSingleContact); textWasSet = true; + } else { + Log.w(TAG, "Filter type \"" + filter.filterType + "\" isn't expected."); } } + } else { + Log.w(TAG, "Filter is null."); } return textWasSet; } + + /** This will update filter via a given ContactListFilterController. */ + public static void handleAccountFilterResult( + ContactListFilterController filterController, int resultCode, Intent data) { + if (resultCode == Activity.RESULT_OK) { + final ContactListFilter filter = data.getParcelableExtra(EXTRA_CONTACT_LIST_FILTER); + if (filter == null) { + return; + } + if (filter.filterType == ContactListFilter.FILTER_TYPE_CUSTOM) { + filterController.selectCustomFilter(); + } else { + filterController.setContactListFilter(filter, true); + } + } + } } diff --git a/java/com/android/contacts/common/util/BitmapUtil.java b/java/com/android/contacts/common/util/BitmapUtil.java index 51f65f280..20f916a3f 100644 --- a/java/com/android/contacts/common/util/BitmapUtil.java +++ b/java/com/android/contacts/common/util/BitmapUtil.java @@ -24,6 +24,8 @@ import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; /** Provides static functions to decode bitmaps at the optimal size */ public class BitmapUtil { @@ -88,6 +90,30 @@ public class BitmapUtil { return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options); } + /** + * Retrieves a copy of the specified drawable resource, rotated by a specified angle. + * + * @param resources The current resources. + * @param resourceId The resource ID of the drawable to rotate. + * @param angle The angle of rotation. + * @return Rotated drawable. + */ + public static Drawable getRotatedDrawable( + android.content.res.Resources resources, int resourceId, float angle) { + + // Get the original drawable and make a copy which will be rotated. + Bitmap original = BitmapFactory.decodeResource(resources, resourceId); + Bitmap rotated = + Bitmap.createBitmap(original.getWidth(), original.getHeight(), Bitmap.Config.ARGB_8888); + + // Perform the rotation. + Canvas tempCanvas = new Canvas(rotated); + tempCanvas.rotate(angle, original.getWidth() / 2, original.getHeight() / 2); + tempCanvas.drawBitmap(original, 0, 0, null); + + return new BitmapDrawable(resources, rotated); + } + /** * Given an input bitmap, scales it to the given width/height and makes it round. * diff --git a/java/com/android/contacts/common/util/ContactDisplayUtils.java b/java/com/android/contacts/common/util/ContactDisplayUtils.java index ff22f2880..1586784db 100644 --- a/java/com/android/contacts/common/util/ContactDisplayUtils.java +++ b/java/com/android/contacts/common/util/ContactDisplayUtils.java @@ -16,20 +16,21 @@ package com.android.contacts.common.util; +import static android.provider.ContactsContract.CommonDataKinds.Phone; + import android.content.Context; import android.content.res.Resources; -import android.provider.ContactsContract.CommonDataKinds.Phone; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.text.Spannable; import android.text.SpannableString; import android.text.TextUtils; import android.text.style.TtsSpan; +import android.util.Log; import android.util.Patterns; import com.android.contacts.common.R; import com.android.contacts.common.compat.PhoneNumberUtilsCompat; import com.android.contacts.common.preference.ContactsPreferences; -import com.android.dialer.common.LogUtil; import java.util.Objects; /** Methods for handling various contact data labels. */ @@ -37,6 +38,7 @@ public class ContactDisplayUtils { public static final int INTERACTION_CALL = 1; public static final int INTERACTION_SMS = 2; + private static final String TAG = ContactDisplayUtils.class.getSimpleName(); /** * Checks if the given data type is a custom type. @@ -72,9 +74,9 @@ public class ContactDisplayUtils { } else { resId = getPhoneLabelResourceId(type); if (interactionType != INTERACTION_CALL) { - LogUtil.e( - "ContactDisplayUtils.getLabelForCallOrSms", - "un-recognized interaction type: " + Log.e( + TAG, + "Un-recognized interaction type: " + interactionType + ". Defaulting to ContactDisplayUtils.INTERACTION_CALL."); } diff --git a/java/com/android/contacts/common/util/DateUtils.java b/java/com/android/contacts/common/util/DateUtils.java index 09d52bce8..1935d727a 100644 --- a/java/com/android/contacts/common/util/DateUtils.java +++ b/java/com/android/contacts/common/util/DateUtils.java @@ -16,11 +16,252 @@ package com.android.contacts.common.util; +import android.content.Context; +import android.text.format.DateFormat; import android.text.format.Time; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; /** Utility methods for processing dates. */ public class DateUtils { + public static final TimeZone UTC_TIMEZONE = TimeZone.getTimeZone("UTC"); + + /** + * When parsing a date without a year, the system assumes 1970, which wasn't a leap-year. Let's + * add a one-off hack for that day of the year + */ + public static final String NO_YEAR_DATE_FEB29TH = "--02-29"; + + // Variations of ISO 8601 date format. Do not change the order - it does affect the + // result in ambiguous cases. + private static final SimpleDateFormat[] DATE_FORMATS = { + CommonDateUtils.FULL_DATE_FORMAT, + CommonDateUtils.DATE_AND_TIME_FORMAT, + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'", Locale.US), + new SimpleDateFormat("yyyyMMdd", Locale.US), + new SimpleDateFormat("yyyyMMdd'T'HHmmssSSS'Z'", Locale.US), + new SimpleDateFormat("yyyyMMdd'T'HHmmss'Z'", Locale.US), + new SimpleDateFormat("yyyyMMdd'T'HHmm'Z'", Locale.US), + }; + + static { + for (SimpleDateFormat format : DATE_FORMATS) { + format.setLenient(true); + format.setTimeZone(UTC_TIMEZONE); + } + CommonDateUtils.NO_YEAR_DATE_FORMAT.setTimeZone(UTC_TIMEZONE); + } + + /** + * Parses the supplied string to see if it looks like a date. + * + * @param string The string representation of the provided date + * @param mustContainYear If true, the string is parsed as a date containing a year. If false, the + * string is parsed into a valid date even if the year field is missing. + * @return A Calendar object corresponding to the date if the string is successfully parsed. If + * not, null is returned. + */ + public static Calendar parseDate(String string, boolean mustContainYear) { + ParsePosition parsePosition = new ParsePosition(0); + Date date; + if (!mustContainYear) { + final boolean noYearParsed; + // Unfortunately, we can't parse Feb 29th correctly, so let's handle this day seperately + if (NO_YEAR_DATE_FEB29TH.equals(string)) { + return getUtcDate(0, Calendar.FEBRUARY, 29); + } else { + synchronized (CommonDateUtils.NO_YEAR_DATE_FORMAT) { + date = CommonDateUtils.NO_YEAR_DATE_FORMAT.parse(string, parsePosition); + } + noYearParsed = parsePosition.getIndex() == string.length(); + } + + if (noYearParsed) { + return getUtcDate(date, true); + } + } + for (int i = 0; i < DATE_FORMATS.length; i++) { + SimpleDateFormat f = DATE_FORMATS[i]; + synchronized (f) { + parsePosition.setIndex(0); + date = f.parse(string, parsePosition); + if (parsePosition.getIndex() == string.length()) { + return getUtcDate(date, false); + } + } + } + return null; + } + + private static final Calendar getUtcDate(Date date, boolean noYear) { + final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US); + calendar.setTime(date); + if (noYear) { + calendar.set(Calendar.YEAR, 0); + } + return calendar; + } + + private static final Calendar getUtcDate(int year, int month, int dayOfMonth) { + final Calendar calendar = Calendar.getInstance(UTC_TIMEZONE, Locale.US); + calendar.clear(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month); + calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); + return calendar; + } + + public static boolean isYearSet(Calendar cal) { + // use the Calendar.YEAR field to track whether or not the year is set instead of + // Calendar.isSet() because doing Calendar.get() causes Calendar.isSet() to become + // true irregardless of what the previous value was + return cal.get(Calendar.YEAR) > 1; + } + + /** + * Same as {@link #formatDate(Context context, String string, boolean longForm)}, with longForm + * set to {@code true} by default. + * + * @param context Valid context + * @param string String representation of a date to parse + * @return Returns the same date in a cleaned up format. If the supplied string does not look like + * a date, return it unchanged. + */ + public static String formatDate(Context context, String string) { + return formatDate(context, string, true); + } + + /** + * Parses the supplied string to see if it looks like a date. + * + * @param context Valid context + * @param string String representation of a date to parse + * @param longForm If true, return the date formatted into its long string representation. If + * false, return the date formatted using its short form representation (i.e. 12/11/2012) + * @return Returns the same date in a cleaned up format. If the supplied string does not look like + * a date, return it unchanged. + */ + public static String formatDate(Context context, String string, boolean longForm) { + if (string == null) { + return null; + } + + string = string.trim(); + if (string.length() == 0) { + return string; + } + final Calendar cal = parseDate(string, false); + + // we weren't able to parse the string successfully so just return it unchanged + if (cal == null) { + return string; + } + + final boolean isYearSet = isYearSet(cal); + final java.text.DateFormat outFormat; + if (!isYearSet) { + outFormat = getLocalizedDateFormatWithoutYear(context); + } else { + outFormat = + longForm ? DateFormat.getLongDateFormat(context) : DateFormat.getDateFormat(context); + } + synchronized (outFormat) { + outFormat.setTimeZone(UTC_TIMEZONE); + return outFormat.format(cal.getTime()); + } + } + + public static boolean isMonthBeforeDay(Context context) { + char[] dateFormatOrder = DateFormat.getDateFormatOrder(context); + for (int i = 0; i < dateFormatOrder.length; i++) { + if (dateFormatOrder[i] == 'd') { + return false; + } + if (dateFormatOrder[i] == 'M') { + return true; + } + } + return false; + } + + /** + * Returns a SimpleDateFormat object without the year fields by using a regular expression to + * eliminate the year in the string pattern. In the rare occurence that the resulting pattern + * cannot be reconverted into a SimpleDateFormat, it uses the provided context to determine + * whether the month field should be displayed before the day field, and returns either "MMMM dd" + * or "dd MMMM" converted into a SimpleDateFormat. + */ + public static java.text.DateFormat getLocalizedDateFormatWithoutYear(Context context) { + final String pattern = + ((SimpleDateFormat) SimpleDateFormat.getDateInstance(java.text.DateFormat.LONG)) + .toPattern(); + // Determine the correct regex pattern for year. + // Special case handling for Spanish locale by checking for "de" + final String yearPattern = + pattern.contains("de") ? "[^Mm]*[Yy]+[^Mm]*" : "[^DdMm]*[Yy]+[^DdMm]*"; + try { + // Eliminate the substring in pattern that matches the format for that of year + return new SimpleDateFormat(pattern.replaceAll(yearPattern, "")); + } catch (IllegalArgumentException e) { + return new SimpleDateFormat(DateUtils.isMonthBeforeDay(context) ? "MMMM dd" : "dd MMMM"); + } + } + + /** + * Given a calendar (possibly containing only a day of the year), returns the earliest possible + * anniversary of the date that is equal to or after the current point in time if the date does + * not contain a year, or the date converted to the local time zone (if the date contains a year. + * + * @param target The date we wish to convert(in the UTC time zone). + * @return If date does not contain a year (year < 1900), returns the next earliest anniversary + * that is after the current point in time (in the local time zone). Otherwise, returns the + * adjusted Date in the local time zone. + */ + public static Date getNextAnnualDate(Calendar target) { + final Calendar today = Calendar.getInstance(); + today.setTime(new Date()); + + // Round the current time to the exact start of today so that when we compare + // today against the target date, both dates are set to exactly 0000H. + today.set(Calendar.HOUR_OF_DAY, 0); + today.set(Calendar.MINUTE, 0); + today.set(Calendar.SECOND, 0); + today.set(Calendar.MILLISECOND, 0); + + final boolean isYearSet = isYearSet(target); + final int targetYear = target.get(Calendar.YEAR); + final int targetMonth = target.get(Calendar.MONTH); + final int targetDay = target.get(Calendar.DAY_OF_MONTH); + final boolean isFeb29 = (targetMonth == Calendar.FEBRUARY && targetDay == 29); + final GregorianCalendar anniversary = new GregorianCalendar(); + // Convert from the UTC date to the local date. Set the year to today's year if the + // there is no provided year (targetYear < 1900) + anniversary.set(!isYearSet ? today.get(Calendar.YEAR) : targetYear, targetMonth, targetDay); + // If the anniversary's date is before the start of today and there is no year set, + // increment the year by 1 so that the returned date is always equal to or greater than + // today. If the day is a leap year, keep going until we get the next leap year anniversary + // Otherwise if there is already a year set, simply return the exact date. + if (!isYearSet) { + int anniversaryYear = today.get(Calendar.YEAR); + if (anniversary.before(today) || (isFeb29 && !anniversary.isLeapYear(anniversaryYear))) { + // If the target date is not Feb 29, then set the anniversary to the next year. + // Otherwise, keep going until we find the next leap year (this is not guaranteed + // to be in 4 years time). + do { + anniversaryYear += 1; + } while (isFeb29 && !anniversary.isLeapYear(anniversaryYear)); + anniversary.set(anniversaryYear, targetMonth, targetDay); + } + } + return anniversary.getTime(); + } + /** * Determine the difference, in days between two dates. Uses similar logic as the {@link * android.text.format.DateUtils.getRelativeTimeSpanString} method. diff --git a/java/com/android/contacts/common/util/MaterialColorMapUtils.java b/java/com/android/contacts/common/util/MaterialColorMapUtils.java index bd32faa51..a2d9847ec 100644 --- a/java/com/android/contacts/common/util/MaterialColorMapUtils.java +++ b/java/com/android/contacts/common/util/MaterialColorMapUtils.java @@ -21,6 +21,7 @@ import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.os.Trace; +import com.android.contacts.common.R; public class MaterialColorMapUtils { @@ -34,6 +35,13 @@ public class MaterialColorMapUtils { resources.obtainTypedArray(com.android.contacts.common.R.array.letter_tile_colors_dark); } + public static MaterialPalette getDefaultPrimaryAndSecondaryColors(Resources resources) { + final int primaryColor = resources.getColor(R.color.quickcontact_default_photo_tint_color); + final int secondaryColor = + resources.getColor(R.color.quickcontact_default_photo_tint_color_dark); + return new MaterialPalette(primaryColor, secondaryColor); + } + /** * Returns the hue component of a color int. * diff --git a/java/com/android/contacts/common/util/NameConverter.java b/java/com/android/contacts/common/util/NameConverter.java new file mode 100644 index 000000000..ae3275d14 --- /dev/null +++ b/java/com/android/contacts/common/util/NameConverter.java @@ -0,0 +1,242 @@ +/* + * 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.common.util; + +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.net.Uri.Builder; +import android.provider.ContactsContract; +import android.provider.ContactsContract.CommonDataKinds.StructuredName; +import android.text.TextUtils; +import com.android.contacts.common.model.dataitem.StructuredNameDataItem; +import java.util.Map; +import java.util.TreeMap; + +/** + * Utility class for converting between a display name and structured name (and vice-versa), via + * calls to the contact provider. + */ +public class NameConverter { + + /** The array of fields that comprise a structured name. */ + public static final String[] STRUCTURED_NAME_FIELDS = + new String[] { + StructuredName.PREFIX, + StructuredName.GIVEN_NAME, + StructuredName.MIDDLE_NAME, + StructuredName.FAMILY_NAME, + StructuredName.SUFFIX + }; + + /** + * Converts the given structured name (provided as a map from {@link StructuredName} fields to + * corresponding values) into a display name string. + * + *

Note that this operates via a call back to the ContactProvider, but it does not access the + * database, so it should be safe to call from the UI thread. See ContactsProvider2.completeName() + * for the underlying method call. + * + * @param context Activity context. + * @param structuredName The structured name map to convert. + * @return The display name computed from the structured name map. + */ + public static String structuredNameToDisplayName( + Context context, Map structuredName) { + Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name"); + for (String key : STRUCTURED_NAME_FIELDS) { + if (structuredName.containsKey(key)) { + appendQueryParameter(builder, key, structuredName.get(key)); + } + } + return fetchDisplayName(context, builder.build()); + } + + /** + * Converts the given structured name (provided as ContentValues) into a display name string. + * + * @param context Activity context. + * @param values The content values containing values comprising the structured name. + */ + public static String structuredNameToDisplayName(Context context, ContentValues values) { + Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name"); + for (String key : STRUCTURED_NAME_FIELDS) { + if (values.containsKey(key)) { + appendQueryParameter(builder, key, values.getAsString(key)); + } + } + return fetchDisplayName(context, builder.build()); + } + + /** Helper method for fetching the display name via the given URI. */ + private static String fetchDisplayName(Context context, Uri uri) { + String displayName = null; + Cursor cursor = + context + .getContentResolver() + .query( + uri, + new String[] { + StructuredName.DISPLAY_NAME, + }, + null, + null, + null); + + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + displayName = cursor.getString(0); + } + } finally { + cursor.close(); + } + } + return displayName; + } + + /** + * Converts the given display name string into a structured name (as a map from {@link + * StructuredName} fields to corresponding values). + * + *

Note that this operates via a call back to the ContactProvider, but it does not access the + * database, so it should be safe to call from the UI thread. + * + * @param context Activity context. + * @param displayName The display name to convert. + * @return The structured name map computed from the display name. + */ + public static Map displayNameToStructuredName( + Context context, String displayName) { + Map structuredName = new TreeMap(); + Builder builder = ContactsContract.AUTHORITY_URI.buildUpon().appendPath("complete_name"); + + appendQueryParameter(builder, StructuredName.DISPLAY_NAME, displayName); + Cursor cursor = + context + .getContentResolver() + .query(builder.build(), STRUCTURED_NAME_FIELDS, null, null, null); + + if (cursor != null) { + try { + if (cursor.moveToFirst()) { + for (int i = 0; i < STRUCTURED_NAME_FIELDS.length; i++) { + structuredName.put(STRUCTURED_NAME_FIELDS[i], cursor.getString(i)); + } + } + } finally { + cursor.close(); + } + } + return structuredName; + } + + /** + * Converts the given display name string into a structured name (inserting the structured values + * into a new or existing ContentValues object). + * + *

Note that this operates via a call back to the ContactProvider, but it does not access the + * database, so it should be safe to call from the UI thread. + * + * @param context Activity context. + * @param displayName The display name to convert. + * @param contentValues The content values object to place the structured name values into. If + * null, a new one will be created and returned. + * @return The ContentValues object containing the structured name fields derived from the display + * name. + */ + public static ContentValues displayNameToStructuredName( + Context context, String displayName, ContentValues contentValues) { + if (contentValues == null) { + contentValues = new ContentValues(); + } + Map mapValues = displayNameToStructuredName(context, displayName); + for (String key : mapValues.keySet()) { + contentValues.put(key, mapValues.get(key)); + } + return contentValues; + } + + private static void appendQueryParameter(Builder builder, String field, String value) { + if (!TextUtils.isEmpty(value)) { + builder.appendQueryParameter(field, value); + } + } + + /** + * Parses phonetic name and returns parsed data (family, middle, given) as ContentValues. Parsed + * data should be {@link StructuredName#PHONETIC_FAMILY_NAME}, {@link + * StructuredName#PHONETIC_MIDDLE_NAME}, and {@link StructuredName#PHONETIC_GIVEN_NAME}. If this + * method cannot parse given phoneticName, null values will be stored. + * + * @param phoneticName Phonetic name to be parsed + * @param values ContentValues to be used for storing data. If null, new instance will be created. + * @return ContentValues with parsed data. Those data can be null. + */ + public static StructuredNameDataItem parsePhoneticName( + String phoneticName, StructuredNameDataItem item) { + String family = null; + String middle = null; + String given = null; + + if (!TextUtils.isEmpty(phoneticName)) { + String[] strings = phoneticName.split(" ", 3); + switch (strings.length) { + case 1: + family = strings[0]; + break; + case 2: + family = strings[0]; + given = strings[1]; + break; + case 3: + family = strings[0]; + middle = strings[1]; + given = strings[2]; + break; + } + } + + if (item == null) { + item = new StructuredNameDataItem(); + } + item.setPhoneticFamilyName(family); + item.setPhoneticMiddleName(middle); + item.setPhoneticGivenName(given); + return item; + } + + /** Constructs and returns a phonetic full name from given parts. */ + public static String buildPhoneticName(String family, String middle, String given) { + if (!TextUtils.isEmpty(family) || !TextUtils.isEmpty(middle) || !TextUtils.isEmpty(given)) { + StringBuilder sb = new StringBuilder(); + if (!TextUtils.isEmpty(family)) { + sb.append(family.trim()).append(' '); + } + if (!TextUtils.isEmpty(middle)) { + sb.append(middle.trim()).append(' '); + } + if (!TextUtils.isEmpty(given)) { + sb.append(given.trim()).append(' '); + } + sb.setLength(sb.length() - 1); // Yank the last space + return sb.toString(); + } else { + return null; + } + } +} diff --git a/java/com/android/contacts/common/util/StopWatch.java b/java/com/android/contacts/common/util/StopWatch.java index 7986d1081..b944b9867 100644 --- a/java/com/android/contacts/common/util/StopWatch.java +++ b/java/com/android/contacts/common/util/StopWatch.java @@ -16,7 +16,7 @@ package com.android.contacts.common.util; -import com.android.dialer.common.LogUtil; +import android.util.Log; import java.util.ArrayList; /** A {@link StopWatch} records start, laps and stop, and print them to logcat. */ @@ -37,6 +37,11 @@ public class StopWatch { return new StopWatch(label); } + /** Return a dummy instance that does no operations. */ + public static StopWatch getNullStopWatch() { + return NullStopWatch.INSTANCE; + } + /** Record a lap. */ public void lap(String lapLabel) { mTimes.add(System.currentTimeMillis()); @@ -71,6 +76,25 @@ public class StopWatch { sb.append(" "); last = current; } - LogUtil.v(TAG, sb.toString()); + Log.v(TAG, sb.toString()); + } + + private static class NullStopWatch extends StopWatch { + + public static final NullStopWatch INSTANCE = new NullStopWatch(); + + public NullStopWatch() { + super(null); + } + + @Override + public void lap(String lapLabel) { + // noop + } + + @Override + public void stopAndLog(String TAG, int timeThresholdToLog) { + // noop + } } } diff --git a/java/com/android/contacts/common/util/TelephonyManagerUtils.java b/java/com/android/contacts/common/util/TelephonyManagerUtils.java index e4c2c6373..b664268ca 100644 --- a/java/com/android/contacts/common/util/TelephonyManagerUtils.java +++ b/java/com/android/contacts/common/util/TelephonyManagerUtils.java @@ -33,4 +33,13 @@ public class TelephonyManagerUtils { final String voiceMailLabel = telephonyManager.getVoiceMailAlphaTag(); return voiceMailLabel; } + + /** + * @param context Current application context. + * @return True if there is a subscription which supports video calls. False otherwise. + */ + public static boolean hasVideoCallSubscription(Context context) { + // TODO: Check the telephony manager's subscriptions to see if any support video calls. + return true; + } } diff --git a/java/com/android/contacts/common/util/TrafficStatsTags.java b/java/com/android/contacts/common/util/TrafficStatsTags.java new file mode 100644 index 000000000..b0e7fb583 --- /dev/null +++ b/java/com/android/contacts/common/util/TrafficStatsTags.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2015 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.common.util; + +public class TrafficStatsTags { + + public static final int CONTACT_PHOTO_DOWNLOAD_TAG = 0x0001; + public static final int TAG_MAX = 0x9999; +} -- cgit v1.2.3